162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Universal Flash Storage Host controller Platform bus based glue driver 462306a36Sopenharmony_ci * Copyright (C) 2011-2013 Samsung India Software Operations 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Authors: 762306a36Sopenharmony_ci * Santosh Yaraganavi <santosh.sy@samsung.com> 862306a36Sopenharmony_ci * Vinayak Holikatti <h.vinayak@samsung.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <ufs/ufshcd.h> 1762306a36Sopenharmony_ci#include "ufshcd-pltfrm.h" 1862306a36Sopenharmony_ci#include <ufs/unipro.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define UFSHCD_DEFAULT_LANES_PER_DIRECTION 2 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic int ufshcd_parse_clock_info(struct ufs_hba *hba) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci int ret = 0; 2562306a36Sopenharmony_ci int cnt; 2662306a36Sopenharmony_ci int i; 2762306a36Sopenharmony_ci struct device *dev = hba->dev; 2862306a36Sopenharmony_ci struct device_node *np = dev->of_node; 2962306a36Sopenharmony_ci const char *name; 3062306a36Sopenharmony_ci u32 *clkfreq = NULL; 3162306a36Sopenharmony_ci struct ufs_clk_info *clki; 3262306a36Sopenharmony_ci int len = 0; 3362306a36Sopenharmony_ci size_t sz = 0; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci if (!np) 3662306a36Sopenharmony_ci goto out; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci cnt = of_property_count_strings(np, "clock-names"); 3962306a36Sopenharmony_ci if (!cnt || (cnt == -EINVAL)) { 4062306a36Sopenharmony_ci dev_info(dev, "%s: Unable to find clocks, assuming enabled\n", 4162306a36Sopenharmony_ci __func__); 4262306a36Sopenharmony_ci } else if (cnt < 0) { 4362306a36Sopenharmony_ci dev_err(dev, "%s: count clock strings failed, err %d\n", 4462306a36Sopenharmony_ci __func__, cnt); 4562306a36Sopenharmony_ci ret = cnt; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if (cnt <= 0) 4962306a36Sopenharmony_ci goto out; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if (!of_get_property(np, "freq-table-hz", &len)) { 5262306a36Sopenharmony_ci dev_info(dev, "freq-table-hz property not specified\n"); 5362306a36Sopenharmony_ci goto out; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (len <= 0) 5762306a36Sopenharmony_ci goto out; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci sz = len / sizeof(*clkfreq); 6062306a36Sopenharmony_ci if (sz != 2 * cnt) { 6162306a36Sopenharmony_ci dev_err(dev, "%s len mismatch\n", "freq-table-hz"); 6262306a36Sopenharmony_ci ret = -EINVAL; 6362306a36Sopenharmony_ci goto out; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci clkfreq = devm_kcalloc(dev, sz, sizeof(*clkfreq), 6762306a36Sopenharmony_ci GFP_KERNEL); 6862306a36Sopenharmony_ci if (!clkfreq) { 6962306a36Sopenharmony_ci ret = -ENOMEM; 7062306a36Sopenharmony_ci goto out; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci ret = of_property_read_u32_array(np, "freq-table-hz", 7462306a36Sopenharmony_ci clkfreq, sz); 7562306a36Sopenharmony_ci if (ret && (ret != -EINVAL)) { 7662306a36Sopenharmony_ci dev_err(dev, "%s: error reading array %d\n", 7762306a36Sopenharmony_ci "freq-table-hz", ret); 7862306a36Sopenharmony_ci return ret; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci for (i = 0; i < sz; i += 2) { 8262306a36Sopenharmony_ci ret = of_property_read_string_index(np, "clock-names", i/2, 8362306a36Sopenharmony_ci &name); 8462306a36Sopenharmony_ci if (ret) 8562306a36Sopenharmony_ci goto out; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL); 8862306a36Sopenharmony_ci if (!clki) { 8962306a36Sopenharmony_ci ret = -ENOMEM; 9062306a36Sopenharmony_ci goto out; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci clki->min_freq = clkfreq[i]; 9462306a36Sopenharmony_ci clki->max_freq = clkfreq[i+1]; 9562306a36Sopenharmony_ci clki->name = devm_kstrdup(dev, name, GFP_KERNEL); 9662306a36Sopenharmony_ci if (!clki->name) { 9762306a36Sopenharmony_ci ret = -ENOMEM; 9862306a36Sopenharmony_ci goto out; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (!strcmp(name, "ref_clk")) 10262306a36Sopenharmony_ci clki->keep_link_active = true; 10362306a36Sopenharmony_ci dev_dbg(dev, "%s: min %u max %u name %s\n", "freq-table-hz", 10462306a36Sopenharmony_ci clki->min_freq, clki->max_freq, clki->name); 10562306a36Sopenharmony_ci list_add_tail(&clki->list, &hba->clk_list_head); 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ciout: 10862306a36Sopenharmony_ci return ret; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic bool phandle_exists(const struct device_node *np, 11262306a36Sopenharmony_ci const char *phandle_name, int index) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct device_node *parse_np = of_parse_phandle(np, phandle_name, index); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (parse_np) 11762306a36Sopenharmony_ci of_node_put(parse_np); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return parse_np != NULL; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci#define MAX_PROP_SIZE 32 12362306a36Sopenharmony_ciint ufshcd_populate_vreg(struct device *dev, const char *name, 12462306a36Sopenharmony_ci struct ufs_vreg **out_vreg) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci char prop_name[MAX_PROP_SIZE]; 12762306a36Sopenharmony_ci struct ufs_vreg *vreg = NULL; 12862306a36Sopenharmony_ci struct device_node *np = dev->of_node; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (!np) { 13162306a36Sopenharmony_ci dev_err(dev, "%s: non DT initialization\n", __func__); 13262306a36Sopenharmony_ci goto out; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", name); 13662306a36Sopenharmony_ci if (!phandle_exists(np, prop_name, 0)) { 13762306a36Sopenharmony_ci dev_info(dev, "%s: Unable to find %s regulator, assuming enabled\n", 13862306a36Sopenharmony_ci __func__, prop_name); 13962306a36Sopenharmony_ci goto out; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL); 14362306a36Sopenharmony_ci if (!vreg) 14462306a36Sopenharmony_ci return -ENOMEM; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci vreg->name = devm_kstrdup(dev, name, GFP_KERNEL); 14762306a36Sopenharmony_ci if (!vreg->name) 14862306a36Sopenharmony_ci return -ENOMEM; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci snprintf(prop_name, MAX_PROP_SIZE, "%s-max-microamp", name); 15162306a36Sopenharmony_ci if (of_property_read_u32(np, prop_name, &vreg->max_uA)) { 15262306a36Sopenharmony_ci dev_info(dev, "%s: unable to find %s\n", __func__, prop_name); 15362306a36Sopenharmony_ci vreg->max_uA = 0; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ciout: 15662306a36Sopenharmony_ci *out_vreg = vreg; 15762306a36Sopenharmony_ci return 0; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_populate_vreg); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/** 16262306a36Sopenharmony_ci * ufshcd_parse_regulator_info - get regulator info from device tree 16362306a36Sopenharmony_ci * @hba: per adapter instance 16462306a36Sopenharmony_ci * 16562306a36Sopenharmony_ci * Get regulator info from device tree for vcc, vccq, vccq2 power supplies. 16662306a36Sopenharmony_ci * If any of the supplies are not defined it is assumed that they are always-on 16762306a36Sopenharmony_ci * and hence return zero. If the property is defined but parsing is failed 16862306a36Sopenharmony_ci * then return corresponding error. 16962306a36Sopenharmony_ci * 17062306a36Sopenharmony_ci * Return: 0 upon success; < 0 upon failure. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_cistatic int ufshcd_parse_regulator_info(struct ufs_hba *hba) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci int err; 17562306a36Sopenharmony_ci struct device *dev = hba->dev; 17662306a36Sopenharmony_ci struct ufs_vreg_info *info = &hba->vreg_info; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci err = ufshcd_populate_vreg(dev, "vdd-hba", &info->vdd_hba); 17962306a36Sopenharmony_ci if (err) 18062306a36Sopenharmony_ci goto out; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci err = ufshcd_populate_vreg(dev, "vcc", &info->vcc); 18362306a36Sopenharmony_ci if (err) 18462306a36Sopenharmony_ci goto out; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci err = ufshcd_populate_vreg(dev, "vccq", &info->vccq); 18762306a36Sopenharmony_ci if (err) 18862306a36Sopenharmony_ci goto out; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci err = ufshcd_populate_vreg(dev, "vccq2", &info->vccq2); 19162306a36Sopenharmony_ciout: 19262306a36Sopenharmony_ci return err; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic void ufshcd_init_lanes_per_dir(struct ufs_hba *hba) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct device *dev = hba->dev; 19862306a36Sopenharmony_ci int ret; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci ret = of_property_read_u32(dev->of_node, "lanes-per-direction", 20162306a36Sopenharmony_ci &hba->lanes_per_direction); 20262306a36Sopenharmony_ci if (ret) { 20362306a36Sopenharmony_ci dev_dbg(hba->dev, 20462306a36Sopenharmony_ci "%s: failed to read lanes-per-direction, ret=%d\n", 20562306a36Sopenharmony_ci __func__, ret); 20662306a36Sopenharmony_ci hba->lanes_per_direction = UFSHCD_DEFAULT_LANES_PER_DIRECTION; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/** 21162306a36Sopenharmony_ci * ufshcd_get_pwr_dev_param - get finally agreed attributes for 21262306a36Sopenharmony_ci * power mode change 21362306a36Sopenharmony_ci * @pltfrm_param: pointer to platform parameters 21462306a36Sopenharmony_ci * @dev_max: pointer to device attributes 21562306a36Sopenharmony_ci * @agreed_pwr: returned agreed attributes 21662306a36Sopenharmony_ci * 21762306a36Sopenharmony_ci * Return: 0 on success, non-zero value on failure. 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ciint ufshcd_get_pwr_dev_param(const struct ufs_dev_params *pltfrm_param, 22062306a36Sopenharmony_ci const struct ufs_pa_layer_attr *dev_max, 22162306a36Sopenharmony_ci struct ufs_pa_layer_attr *agreed_pwr) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci int min_pltfrm_gear; 22462306a36Sopenharmony_ci int min_dev_gear; 22562306a36Sopenharmony_ci bool is_dev_sup_hs = false; 22662306a36Sopenharmony_ci bool is_pltfrm_max_hs = false; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (dev_max->pwr_rx == FAST_MODE) 22962306a36Sopenharmony_ci is_dev_sup_hs = true; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (pltfrm_param->desired_working_mode == UFS_HS_MODE) { 23262306a36Sopenharmony_ci is_pltfrm_max_hs = true; 23362306a36Sopenharmony_ci min_pltfrm_gear = min_t(u32, pltfrm_param->hs_rx_gear, 23462306a36Sopenharmony_ci pltfrm_param->hs_tx_gear); 23562306a36Sopenharmony_ci } else { 23662306a36Sopenharmony_ci min_pltfrm_gear = min_t(u32, pltfrm_param->pwm_rx_gear, 23762306a36Sopenharmony_ci pltfrm_param->pwm_tx_gear); 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* 24162306a36Sopenharmony_ci * device doesn't support HS but 24262306a36Sopenharmony_ci * pltfrm_param->desired_working_mode is HS, 24362306a36Sopenharmony_ci * thus device and pltfrm_param don't agree 24462306a36Sopenharmony_ci */ 24562306a36Sopenharmony_ci if (!is_dev_sup_hs && is_pltfrm_max_hs) { 24662306a36Sopenharmony_ci pr_info("%s: device doesn't support HS\n", 24762306a36Sopenharmony_ci __func__); 24862306a36Sopenharmony_ci return -ENOTSUPP; 24962306a36Sopenharmony_ci } else if (is_dev_sup_hs && is_pltfrm_max_hs) { 25062306a36Sopenharmony_ci /* 25162306a36Sopenharmony_ci * since device supports HS, it supports FAST_MODE. 25262306a36Sopenharmony_ci * since pltfrm_param->desired_working_mode is also HS 25362306a36Sopenharmony_ci * then final decision (FAST/FASTAUTO) is done according 25462306a36Sopenharmony_ci * to pltfrm_params as it is the restricting factor 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_ci agreed_pwr->pwr_rx = pltfrm_param->rx_pwr_hs; 25762306a36Sopenharmony_ci agreed_pwr->pwr_tx = agreed_pwr->pwr_rx; 25862306a36Sopenharmony_ci } else { 25962306a36Sopenharmony_ci /* 26062306a36Sopenharmony_ci * here pltfrm_param->desired_working_mode is PWM. 26162306a36Sopenharmony_ci * it doesn't matter whether device supports HS or PWM, 26262306a36Sopenharmony_ci * in both cases pltfrm_param->desired_working_mode will 26362306a36Sopenharmony_ci * determine the mode 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_ci agreed_pwr->pwr_rx = pltfrm_param->rx_pwr_pwm; 26662306a36Sopenharmony_ci agreed_pwr->pwr_tx = agreed_pwr->pwr_rx; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* 27062306a36Sopenharmony_ci * we would like tx to work in the minimum number of lanes 27162306a36Sopenharmony_ci * between device capability and vendor preferences. 27262306a36Sopenharmony_ci * the same decision will be made for rx 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_ci agreed_pwr->lane_tx = min_t(u32, dev_max->lane_tx, 27562306a36Sopenharmony_ci pltfrm_param->tx_lanes); 27662306a36Sopenharmony_ci agreed_pwr->lane_rx = min_t(u32, dev_max->lane_rx, 27762306a36Sopenharmony_ci pltfrm_param->rx_lanes); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* device maximum gear is the minimum between device rx and tx gears */ 28062306a36Sopenharmony_ci min_dev_gear = min_t(u32, dev_max->gear_rx, dev_max->gear_tx); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* 28362306a36Sopenharmony_ci * if both device capabilities and vendor pre-defined preferences are 28462306a36Sopenharmony_ci * both HS or both PWM then set the minimum gear to be the chosen 28562306a36Sopenharmony_ci * working gear. 28662306a36Sopenharmony_ci * if one is PWM and one is HS then the one that is PWM get to decide 28762306a36Sopenharmony_ci * what is the gear, as it is the one that also decided previously what 28862306a36Sopenharmony_ci * pwr the device will be configured to. 28962306a36Sopenharmony_ci */ 29062306a36Sopenharmony_ci if ((is_dev_sup_hs && is_pltfrm_max_hs) || 29162306a36Sopenharmony_ci (!is_dev_sup_hs && !is_pltfrm_max_hs)) { 29262306a36Sopenharmony_ci agreed_pwr->gear_rx = 29362306a36Sopenharmony_ci min_t(u32, min_dev_gear, min_pltfrm_gear); 29462306a36Sopenharmony_ci } else if (!is_dev_sup_hs) { 29562306a36Sopenharmony_ci agreed_pwr->gear_rx = min_dev_gear; 29662306a36Sopenharmony_ci } else { 29762306a36Sopenharmony_ci agreed_pwr->gear_rx = min_pltfrm_gear; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci agreed_pwr->gear_tx = agreed_pwr->gear_rx; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci agreed_pwr->hs_rate = pltfrm_param->hs_rate; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return 0; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_get_pwr_dev_param); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_civoid ufshcd_init_pwr_dev_param(struct ufs_dev_params *dev_param) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci *dev_param = (struct ufs_dev_params){ 31062306a36Sopenharmony_ci .tx_lanes = UFS_LANE_2, 31162306a36Sopenharmony_ci .rx_lanes = UFS_LANE_2, 31262306a36Sopenharmony_ci .hs_rx_gear = UFS_HS_G3, 31362306a36Sopenharmony_ci .hs_tx_gear = UFS_HS_G3, 31462306a36Sopenharmony_ci .pwm_rx_gear = UFS_PWM_G4, 31562306a36Sopenharmony_ci .pwm_tx_gear = UFS_PWM_G4, 31662306a36Sopenharmony_ci .rx_pwr_pwm = SLOW_MODE, 31762306a36Sopenharmony_ci .tx_pwr_pwm = SLOW_MODE, 31862306a36Sopenharmony_ci .rx_pwr_hs = FAST_MODE, 31962306a36Sopenharmony_ci .tx_pwr_hs = FAST_MODE, 32062306a36Sopenharmony_ci .hs_rate = PA_HS_MODE_B, 32162306a36Sopenharmony_ci .desired_working_mode = UFS_HS_MODE, 32262306a36Sopenharmony_ci }; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_init_pwr_dev_param); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci/** 32762306a36Sopenharmony_ci * ufshcd_pltfrm_init - probe routine of the driver 32862306a36Sopenharmony_ci * @pdev: pointer to Platform device handle 32962306a36Sopenharmony_ci * @vops: pointer to variant ops 33062306a36Sopenharmony_ci * 33162306a36Sopenharmony_ci * Return: 0 on success, non-zero value on failure. 33262306a36Sopenharmony_ci */ 33362306a36Sopenharmony_ciint ufshcd_pltfrm_init(struct platform_device *pdev, 33462306a36Sopenharmony_ci const struct ufs_hba_variant_ops *vops) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct ufs_hba *hba; 33762306a36Sopenharmony_ci void __iomem *mmio_base; 33862306a36Sopenharmony_ci int irq, err; 33962306a36Sopenharmony_ci struct device *dev = &pdev->dev; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci mmio_base = devm_platform_ioremap_resource(pdev, 0); 34262306a36Sopenharmony_ci if (IS_ERR(mmio_base)) { 34362306a36Sopenharmony_ci err = PTR_ERR(mmio_base); 34462306a36Sopenharmony_ci goto out; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 34862306a36Sopenharmony_ci if (irq < 0) { 34962306a36Sopenharmony_ci err = irq; 35062306a36Sopenharmony_ci goto out; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci err = ufshcd_alloc_host(dev, &hba); 35462306a36Sopenharmony_ci if (err) { 35562306a36Sopenharmony_ci dev_err(dev, "Allocation failed\n"); 35662306a36Sopenharmony_ci goto out; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci hba->vops = vops; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci err = ufshcd_parse_clock_info(hba); 36262306a36Sopenharmony_ci if (err) { 36362306a36Sopenharmony_ci dev_err(dev, "%s: clock parse failed %d\n", 36462306a36Sopenharmony_ci __func__, err); 36562306a36Sopenharmony_ci goto dealloc_host; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci err = ufshcd_parse_regulator_info(hba); 36862306a36Sopenharmony_ci if (err) { 36962306a36Sopenharmony_ci dev_err(dev, "%s: regulator init failed %d\n", 37062306a36Sopenharmony_ci __func__, err); 37162306a36Sopenharmony_ci goto dealloc_host; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci ufshcd_init_lanes_per_dir(hba); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci err = ufshcd_init(hba, mmio_base, irq); 37762306a36Sopenharmony_ci if (err) { 37862306a36Sopenharmony_ci dev_err_probe(dev, err, "Initialization failed with error %d\n", 37962306a36Sopenharmony_ci err); 38062306a36Sopenharmony_ci goto dealloc_host; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci pm_runtime_set_active(dev); 38462306a36Sopenharmony_ci pm_runtime_enable(dev); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci return 0; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cidealloc_host: 38962306a36Sopenharmony_ci ufshcd_dealloc_host(hba); 39062306a36Sopenharmony_ciout: 39162306a36Sopenharmony_ci return err; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_pltfrm_init); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ciMODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>"); 39662306a36Sopenharmony_ciMODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>"); 39762306a36Sopenharmony_ciMODULE_DESCRIPTION("UFS host controller Platform bus based glue driver"); 39862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 399