Bean Huo | 6735111 | 2020-06-05 22:05:19 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
Vinayak Holikatti | 03b1781 | 2013-02-26 18:04:45 +0530 | [diff] [blame] | 2 | /* |
| 3 | * Universal Flash Storage Host controller Platform bus based glue driver |
Vinayak Holikatti | 03b1781 | 2013-02-26 18:04:45 +0530 | [diff] [blame] | 4 | * Copyright (C) 2011-2013 Samsung India Software Operations |
| 5 | * |
| 6 | * Authors: |
| 7 | * Santosh Yaraganavi <santosh.sy@samsung.com> |
| 8 | * Vinayak Holikatti <h.vinayak@samsung.com> |
Vinayak Holikatti | 03b1781 | 2013-02-26 18:04:45 +0530 | [diff] [blame] | 9 | */ |
| 10 | |
Vinayak Holikatti | 03b1781 | 2013-02-26 18:04:45 +0530 | [diff] [blame] | 11 | #include <linux/platform_device.h> |
Sujit Reddy Thumma | 6269473 | 2013-07-30 00:36:00 +0530 | [diff] [blame] | 12 | #include <linux/pm_runtime.h> |
Sujit Reddy Thumma | 5c0c28a | 2014-09-25 15:32:21 +0300 | [diff] [blame] | 13 | #include <linux/of.h> |
Vinayak Holikatti | 03b1781 | 2013-02-26 18:04:45 +0530 | [diff] [blame] | 14 | |
Seungwon Jeon | 2953f85 | 2013-06-27 13:31:54 +0900 | [diff] [blame] | 15 | #include "ufshcd.h" |
Yaniv Gardi | 47555a5 | 2015-10-28 13:15:49 +0200 | [diff] [blame] | 16 | #include "ufshcd-pltfrm.h" |
Stanley Chu | 58b60a9 | 2019-03-16 13:04:41 +0800 | [diff] [blame] | 17 | #include "unipro.h" |
Sujit Reddy Thumma | 5c0c28a | 2014-09-25 15:32:21 +0300 | [diff] [blame] | 18 | |
Yaniv Gardi | 54b879b | 2016-03-10 17:37:05 +0200 | [diff] [blame] | 19 | #define UFSHCD_DEFAULT_LANES_PER_DIRECTION 2 |
| 20 | |
Sujit Reddy Thumma | c6e79da | 2014-09-25 15:32:23 +0300 | [diff] [blame] | 21 | static int ufshcd_parse_clock_info(struct ufs_hba *hba) |
| 22 | { |
| 23 | int ret = 0; |
| 24 | int cnt; |
| 25 | int i; |
| 26 | struct device *dev = hba->dev; |
| 27 | struct device_node *np = dev->of_node; |
| 28 | char *name; |
| 29 | u32 *clkfreq = NULL; |
| 30 | struct ufs_clk_info *clki; |
Sahitya Tummala | 4cff6d99 | 2014-09-25 15:32:33 +0300 | [diff] [blame] | 31 | int len = 0; |
| 32 | size_t sz = 0; |
Sujit Reddy Thumma | c6e79da | 2014-09-25 15:32:23 +0300 | [diff] [blame] | 33 | |
| 34 | if (!np) |
| 35 | goto out; |
| 36 | |
Sujit Reddy Thumma | c6e79da | 2014-09-25 15:32:23 +0300 | [diff] [blame] | 37 | cnt = of_property_count_strings(np, "clock-names"); |
| 38 | if (!cnt || (cnt == -EINVAL)) { |
| 39 | dev_info(dev, "%s: Unable to find clocks, assuming enabled\n", |
| 40 | __func__); |
| 41 | } else if (cnt < 0) { |
| 42 | dev_err(dev, "%s: count clock strings failed, err %d\n", |
| 43 | __func__, cnt); |
| 44 | ret = cnt; |
| 45 | } |
| 46 | |
| 47 | if (cnt <= 0) |
| 48 | goto out; |
| 49 | |
Sahitya Tummala | 4cff6d99 | 2014-09-25 15:32:33 +0300 | [diff] [blame] | 50 | if (!of_get_property(np, "freq-table-hz", &len)) { |
| 51 | dev_info(dev, "freq-table-hz property not specified\n"); |
| 52 | goto out; |
| 53 | } |
| 54 | |
| 55 | if (len <= 0) |
| 56 | goto out; |
| 57 | |
| 58 | sz = len / sizeof(*clkfreq); |
| 59 | if (sz != 2 * cnt) { |
| 60 | dev_err(dev, "%s len mismatch\n", "freq-table-hz"); |
| 61 | ret = -EINVAL; |
| 62 | goto out; |
| 63 | } |
| 64 | |
Kees Cook | a86854d | 2018-06-12 14:07:58 -0700 | [diff] [blame] | 65 | clkfreq = devm_kcalloc(dev, sz, sizeof(*clkfreq), |
| 66 | GFP_KERNEL); |
Sujit Reddy Thumma | c6e79da | 2014-09-25 15:32:23 +0300 | [diff] [blame] | 67 | if (!clkfreq) { |
| 68 | ret = -ENOMEM; |
Sujit Reddy Thumma | c6e79da | 2014-09-25 15:32:23 +0300 | [diff] [blame] | 69 | goto out; |
| 70 | } |
| 71 | |
Sahitya Tummala | 4cff6d99 | 2014-09-25 15:32:33 +0300 | [diff] [blame] | 72 | ret = of_property_read_u32_array(np, "freq-table-hz", |
| 73 | clkfreq, sz); |
Sujit Reddy Thumma | c6e79da | 2014-09-25 15:32:23 +0300 | [diff] [blame] | 74 | if (ret && (ret != -EINVAL)) { |
Sahitya Tummala | 4cff6d99 | 2014-09-25 15:32:33 +0300 | [diff] [blame] | 75 | dev_err(dev, "%s: error reading array %d\n", |
| 76 | "freq-table-hz", ret); |
Dolev Raviv | e8cb64d | 2014-10-23 13:25:17 +0300 | [diff] [blame] | 77 | return ret; |
Sujit Reddy Thumma | c6e79da | 2014-09-25 15:32:23 +0300 | [diff] [blame] | 78 | } |
| 79 | |
Sahitya Tummala | 4cff6d99 | 2014-09-25 15:32:33 +0300 | [diff] [blame] | 80 | for (i = 0; i < sz; i += 2) { |
Sujit Reddy Thumma | c6e79da | 2014-09-25 15:32:23 +0300 | [diff] [blame] | 81 | ret = of_property_read_string_index(np, |
Sahitya Tummala | 4cff6d99 | 2014-09-25 15:32:33 +0300 | [diff] [blame] | 82 | "clock-names", i/2, (const char **)&name); |
Sujit Reddy Thumma | c6e79da | 2014-09-25 15:32:23 +0300 | [diff] [blame] | 83 | if (ret) |
Dolev Raviv | e8cb64d | 2014-10-23 13:25:17 +0300 | [diff] [blame] | 84 | goto out; |
Sujit Reddy Thumma | c6e79da | 2014-09-25 15:32:23 +0300 | [diff] [blame] | 85 | |
| 86 | clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL); |
| 87 | if (!clki) { |
| 88 | ret = -ENOMEM; |
Dolev Raviv | e8cb64d | 2014-10-23 13:25:17 +0300 | [diff] [blame] | 89 | goto out; |
Sujit Reddy Thumma | c6e79da | 2014-09-25 15:32:23 +0300 | [diff] [blame] | 90 | } |
| 91 | |
Sahitya Tummala | 4cff6d99 | 2014-09-25 15:32:33 +0300 | [diff] [blame] | 92 | clki->min_freq = clkfreq[i]; |
| 93 | clki->max_freq = clkfreq[i+1]; |
Srinivas Kandagatla | b6ca770 | 2021-09-14 10:22:14 +0100 | [diff] [blame] | 94 | clki->name = devm_kstrdup(dev, name, GFP_KERNEL); |
Can Guo | 81309c2 | 2020-11-25 18:01:00 -0800 | [diff] [blame] | 95 | if (!strcmp(name, "ref_clk")) |
| 96 | clki->keep_link_active = true; |
Sahitya Tummala | 4cff6d99 | 2014-09-25 15:32:33 +0300 | [diff] [blame] | 97 | dev_dbg(dev, "%s: min %u max %u name %s\n", "freq-table-hz", |
| 98 | clki->min_freq, clki->max_freq, clki->name); |
Sujit Reddy Thumma | c6e79da | 2014-09-25 15:32:23 +0300 | [diff] [blame] | 99 | list_add_tail(&clki->list, &hba->clk_list_head); |
| 100 | } |
Sahitya Tummala | 4cff6d99 | 2014-09-25 15:32:33 +0300 | [diff] [blame] | 101 | out: |
Sujit Reddy Thumma | c6e79da | 2014-09-25 15:32:23 +0300 | [diff] [blame] | 102 | return ret; |
| 103 | } |
| 104 | |
Sujit Reddy Thumma | aa49761 | 2014-09-25 15:32:22 +0300 | [diff] [blame] | 105 | #define MAX_PROP_SIZE 32 |
| 106 | static int ufshcd_populate_vreg(struct device *dev, const char *name, |
| 107 | struct ufs_vreg **out_vreg) |
| 108 | { |
Sujit Reddy Thumma | aa49761 | 2014-09-25 15:32:22 +0300 | [diff] [blame] | 109 | char prop_name[MAX_PROP_SIZE]; |
| 110 | struct ufs_vreg *vreg = NULL; |
| 111 | struct device_node *np = dev->of_node; |
| 112 | |
| 113 | if (!np) { |
| 114 | dev_err(dev, "%s: non DT initialization\n", __func__); |
| 115 | goto out; |
| 116 | } |
| 117 | |
| 118 | snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", name); |
| 119 | if (!of_parse_phandle(np, prop_name, 0)) { |
| 120 | dev_info(dev, "%s: Unable to find %s regulator, assuming enabled\n", |
| 121 | __func__, prop_name); |
| 122 | goto out; |
| 123 | } |
| 124 | |
| 125 | vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL); |
Dolev Raviv | 758581b | 2014-10-23 13:25:15 +0300 | [diff] [blame] | 126 | if (!vreg) |
| 127 | return -ENOMEM; |
Sujit Reddy Thumma | aa49761 | 2014-09-25 15:32:22 +0300 | [diff] [blame] | 128 | |
Srinivas Kandagatla | b6ca770 | 2021-09-14 10:22:14 +0100 | [diff] [blame] | 129 | vreg->name = devm_kstrdup(dev, name, GFP_KERNEL); |
Sujit Reddy Thumma | aa49761 | 2014-09-25 15:32:22 +0300 | [diff] [blame] | 130 | |
| 131 | snprintf(prop_name, MAX_PROP_SIZE, "%s-max-microamp", name); |
Stanley Chu | 12e3ffb | 2019-03-28 17:16:26 +0800 | [diff] [blame] | 132 | if (of_property_read_u32(np, prop_name, &vreg->max_uA)) { |
| 133 | dev_info(dev, "%s: unable to find %s\n", __func__, prop_name); |
| 134 | vreg->max_uA = 0; |
Sujit Reddy Thumma | aa49761 | 2014-09-25 15:32:22 +0300 | [diff] [blame] | 135 | } |
Sujit Reddy Thumma | aa49761 | 2014-09-25 15:32:22 +0300 | [diff] [blame] | 136 | out: |
Yue Hu | 2a8561b | 2021-03-10 16:27:41 +0800 | [diff] [blame] | 137 | *out_vreg = vreg; |
| 138 | return 0; |
Sujit Reddy Thumma | aa49761 | 2014-09-25 15:32:22 +0300 | [diff] [blame] | 139 | } |
| 140 | |
| 141 | /** |
| 142 | * ufshcd_parse_regulator_info - get regulator info from device tree |
| 143 | * @hba: per adapter instance |
| 144 | * |
| 145 | * Get regulator info from device tree for vcc, vccq, vccq2 power supplies. |
| 146 | * If any of the supplies are not defined it is assumed that they are always-on |
| 147 | * and hence return zero. If the property is defined but parsing is failed |
| 148 | * then return corresponding error. |
| 149 | */ |
| 150 | static int ufshcd_parse_regulator_info(struct ufs_hba *hba) |
| 151 | { |
| 152 | int err; |
| 153 | struct device *dev = hba->dev; |
| 154 | struct ufs_vreg_info *info = &hba->vreg_info; |
| 155 | |
Raviv Shvili | 6a771a6 | 2014-09-25 15:32:24 +0300 | [diff] [blame] | 156 | err = ufshcd_populate_vreg(dev, "vdd-hba", &info->vdd_hba); |
| 157 | if (err) |
| 158 | goto out; |
| 159 | |
Sujit Reddy Thumma | aa49761 | 2014-09-25 15:32:22 +0300 | [diff] [blame] | 160 | err = ufshcd_populate_vreg(dev, "vcc", &info->vcc); |
| 161 | if (err) |
| 162 | goto out; |
| 163 | |
| 164 | err = ufshcd_populate_vreg(dev, "vccq", &info->vccq); |
| 165 | if (err) |
| 166 | goto out; |
| 167 | |
| 168 | err = ufshcd_populate_vreg(dev, "vccq2", &info->vccq2); |
| 169 | out: |
| 170 | return err; |
| 171 | } |
| 172 | |
Yaniv Gardi | 47555a5 | 2015-10-28 13:15:49 +0200 | [diff] [blame] | 173 | void ufshcd_pltfrm_shutdown(struct platform_device *pdev) |
Subhash Jadavani | 57d104c | 2014-09-25 15:32:30 +0300 | [diff] [blame] | 174 | { |
| 175 | ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev)); |
| 176 | } |
Yaniv Gardi | 47555a5 | 2015-10-28 13:15:49 +0200 | [diff] [blame] | 177 | EXPORT_SYMBOL_GPL(ufshcd_pltfrm_shutdown); |
Subhash Jadavani | 57d104c | 2014-09-25 15:32:30 +0300 | [diff] [blame] | 178 | |
Yaniv Gardi | 54b879b | 2016-03-10 17:37:05 +0200 | [diff] [blame] | 179 | static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba) |
| 180 | { |
| 181 | struct device *dev = hba->dev; |
| 182 | int ret; |
| 183 | |
| 184 | ret = of_property_read_u32(dev->of_node, "lanes-per-direction", |
| 185 | &hba->lanes_per_direction); |
| 186 | if (ret) { |
| 187 | dev_dbg(hba->dev, |
| 188 | "%s: failed to read lanes-per-direction, ret=%d\n", |
| 189 | __func__, ret); |
| 190 | hba->lanes_per_direction = UFSHCD_DEFAULT_LANES_PER_DIRECTION; |
| 191 | } |
| 192 | } |
| 193 | |
Vinayak Holikatti | 03b1781 | 2013-02-26 18:04:45 +0530 | [diff] [blame] | 194 | /** |
Stanley Chu | 58b60a9 | 2019-03-16 13:04:41 +0800 | [diff] [blame] | 195 | * ufshcd_get_pwr_dev_param - get finally agreed attributes for |
| 196 | * power mode change |
| 197 | * @pltfrm_param: pointer to platform parameters |
| 198 | * @dev_max: pointer to device attributes |
| 199 | * @agreed_pwr: returned agreed attributes |
| 200 | * |
| 201 | * Returns 0 on success, non-zero value on failure |
| 202 | */ |
| 203 | int ufshcd_get_pwr_dev_param(struct ufs_dev_params *pltfrm_param, |
| 204 | struct ufs_pa_layer_attr *dev_max, |
| 205 | struct ufs_pa_layer_attr *agreed_pwr) |
| 206 | { |
| 207 | int min_pltfrm_gear; |
| 208 | int min_dev_gear; |
| 209 | bool is_dev_sup_hs = false; |
| 210 | bool is_pltfrm_max_hs = false; |
| 211 | |
| 212 | if (dev_max->pwr_rx == FAST_MODE) |
| 213 | is_dev_sup_hs = true; |
| 214 | |
| 215 | if (pltfrm_param->desired_working_mode == UFS_HS_MODE) { |
| 216 | is_pltfrm_max_hs = true; |
| 217 | min_pltfrm_gear = min_t(u32, pltfrm_param->hs_rx_gear, |
| 218 | pltfrm_param->hs_tx_gear); |
| 219 | } else { |
| 220 | min_pltfrm_gear = min_t(u32, pltfrm_param->pwm_rx_gear, |
| 221 | pltfrm_param->pwm_tx_gear); |
| 222 | } |
| 223 | |
| 224 | /* |
| 225 | * device doesn't support HS but |
| 226 | * pltfrm_param->desired_working_mode is HS, |
| 227 | * thus device and pltfrm_param don't agree |
| 228 | */ |
| 229 | if (!is_dev_sup_hs && is_pltfrm_max_hs) { |
| 230 | pr_info("%s: device doesn't support HS\n", |
| 231 | __func__); |
| 232 | return -ENOTSUPP; |
| 233 | } else if (is_dev_sup_hs && is_pltfrm_max_hs) { |
| 234 | /* |
| 235 | * since device supports HS, it supports FAST_MODE. |
| 236 | * since pltfrm_param->desired_working_mode is also HS |
| 237 | * then final decision (FAST/FASTAUTO) is done according |
| 238 | * to pltfrm_params as it is the restricting factor |
| 239 | */ |
| 240 | agreed_pwr->pwr_rx = pltfrm_param->rx_pwr_hs; |
| 241 | agreed_pwr->pwr_tx = agreed_pwr->pwr_rx; |
| 242 | } else { |
| 243 | /* |
| 244 | * here pltfrm_param->desired_working_mode is PWM. |
| 245 | * it doesn't matter whether device supports HS or PWM, |
| 246 | * in both cases pltfrm_param->desired_working_mode will |
| 247 | * determine the mode |
| 248 | */ |
| 249 | agreed_pwr->pwr_rx = pltfrm_param->rx_pwr_pwm; |
| 250 | agreed_pwr->pwr_tx = agreed_pwr->pwr_rx; |
| 251 | } |
| 252 | |
| 253 | /* |
| 254 | * we would like tx to work in the minimum number of lanes |
| 255 | * between device capability and vendor preferences. |
| 256 | * the same decision will be made for rx |
| 257 | */ |
| 258 | agreed_pwr->lane_tx = min_t(u32, dev_max->lane_tx, |
| 259 | pltfrm_param->tx_lanes); |
| 260 | agreed_pwr->lane_rx = min_t(u32, dev_max->lane_rx, |
| 261 | pltfrm_param->rx_lanes); |
| 262 | |
| 263 | /* device maximum gear is the minimum between device rx and tx gears */ |
| 264 | min_dev_gear = min_t(u32, dev_max->gear_rx, dev_max->gear_tx); |
| 265 | |
| 266 | /* |
| 267 | * if both device capabilities and vendor pre-defined preferences are |
| 268 | * both HS or both PWM then set the minimum gear to be the chosen |
| 269 | * working gear. |
| 270 | * if one is PWM and one is HS then the one that is PWM get to decide |
| 271 | * what is the gear, as it is the one that also decided previously what |
| 272 | * pwr the device will be configured to. |
| 273 | */ |
| 274 | if ((is_dev_sup_hs && is_pltfrm_max_hs) || |
| 275 | (!is_dev_sup_hs && !is_pltfrm_max_hs)) { |
| 276 | agreed_pwr->gear_rx = |
| 277 | min_t(u32, min_dev_gear, min_pltfrm_gear); |
| 278 | } else if (!is_dev_sup_hs) { |
| 279 | agreed_pwr->gear_rx = min_dev_gear; |
| 280 | } else { |
| 281 | agreed_pwr->gear_rx = min_pltfrm_gear; |
| 282 | } |
| 283 | agreed_pwr->gear_tx = agreed_pwr->gear_rx; |
| 284 | |
| 285 | agreed_pwr->hs_rate = pltfrm_param->hs_rate; |
| 286 | |
| 287 | return 0; |
| 288 | } |
| 289 | EXPORT_SYMBOL_GPL(ufshcd_get_pwr_dev_param); |
| 290 | |
Stanley Chu | 6585801 | 2020-11-16 14:50:47 +0800 | [diff] [blame] | 291 | void ufshcd_init_pwr_dev_param(struct ufs_dev_params *dev_param) |
| 292 | { |
| 293 | dev_param->tx_lanes = 2; |
| 294 | dev_param->rx_lanes = 2; |
| 295 | dev_param->hs_rx_gear = UFS_HS_G3; |
| 296 | dev_param->hs_tx_gear = UFS_HS_G3; |
| 297 | dev_param->pwm_rx_gear = UFS_PWM_G4; |
| 298 | dev_param->pwm_tx_gear = UFS_PWM_G4; |
| 299 | dev_param->rx_pwr_pwm = SLOW_MODE; |
| 300 | dev_param->tx_pwr_pwm = SLOW_MODE; |
| 301 | dev_param->rx_pwr_hs = FAST_MODE; |
| 302 | dev_param->tx_pwr_hs = FAST_MODE; |
| 303 | dev_param->hs_rate = PA_HS_MODE_B; |
| 304 | dev_param->desired_working_mode = UFS_HS_MODE; |
| 305 | } |
| 306 | EXPORT_SYMBOL_GPL(ufshcd_init_pwr_dev_param); |
| 307 | |
Stanley Chu | 58b60a9 | 2019-03-16 13:04:41 +0800 | [diff] [blame] | 308 | /** |
Yaniv Gardi | 47555a5 | 2015-10-28 13:15:49 +0200 | [diff] [blame] | 309 | * ufshcd_pltfrm_init - probe routine of the driver |
Vinayak Holikatti | 03b1781 | 2013-02-26 18:04:45 +0530 | [diff] [blame] | 310 | * @pdev: pointer to Platform device handle |
Yaniv Gardi | 47555a5 | 2015-10-28 13:15:49 +0200 | [diff] [blame] | 311 | * @vops: pointer to variant ops |
Vinayak Holikatti | 03b1781 | 2013-02-26 18:04:45 +0530 | [diff] [blame] | 312 | * |
| 313 | * Returns 0 on success, non-zero value on failure |
| 314 | */ |
Yaniv Gardi | 47555a5 | 2015-10-28 13:15:49 +0200 | [diff] [blame] | 315 | int ufshcd_pltfrm_init(struct platform_device *pdev, |
Arnd Bergmann | 176eb92 | 2019-03-04 20:39:11 +0100 | [diff] [blame] | 316 | const struct ufs_hba_variant_ops *vops) |
Vinayak Holikatti | 03b1781 | 2013-02-26 18:04:45 +0530 | [diff] [blame] | 317 | { |
| 318 | struct ufs_hba *hba; |
| 319 | void __iomem *mmio_base; |
Seungwon Jeon | 2953f85 | 2013-06-27 13:31:54 +0900 | [diff] [blame] | 320 | int irq, err; |
Vinayak Holikatti | 03b1781 | 2013-02-26 18:04:45 +0530 | [diff] [blame] | 321 | struct device *dev = &pdev->dev; |
| 322 | |
YueHaibing | 0b27555 | 2019-09-04 21:03:48 +0800 | [diff] [blame] | 323 | mmio_base = devm_platform_ioremap_resource(pdev, 0); |
Tomas Winkler | 645b8ef | 2017-03-14 14:19:36 +0200 | [diff] [blame] | 324 | if (IS_ERR(mmio_base)) { |
| 325 | err = PTR_ERR(mmio_base); |
Seungwon Jeon | 2953f85 | 2013-06-27 13:31:54 +0900 | [diff] [blame] | 326 | goto out; |
Vinayak Holikatti | 03b1781 | 2013-02-26 18:04:45 +0530 | [diff] [blame] | 327 | } |
| 328 | |
Seungwon Jeon | 2953f85 | 2013-06-27 13:31:54 +0900 | [diff] [blame] | 329 | irq = platform_get_irq(pdev, 0); |
| 330 | if (irq < 0) { |
Sergey Shtylyov | 339c9b6 | 2021-03-29 23:50:58 +0300 | [diff] [blame] | 331 | err = irq; |
Seungwon Jeon | 2953f85 | 2013-06-27 13:31:54 +0900 | [diff] [blame] | 332 | goto out; |
Vinayak Holikatti | 03b1781 | 2013-02-26 18:04:45 +0530 | [diff] [blame] | 333 | } |
| 334 | |
Sujit Reddy Thumma | 5c0c28a | 2014-09-25 15:32:21 +0300 | [diff] [blame] | 335 | err = ufshcd_alloc_host(dev, &hba); |
| 336 | if (err) { |
| 337 | dev_err(&pdev->dev, "Allocation failed\n"); |
| 338 | goto out; |
| 339 | } |
| 340 | |
Yaniv Gardi | 47555a5 | 2015-10-28 13:15:49 +0200 | [diff] [blame] | 341 | hba->vops = vops; |
Sujit Reddy Thumma | 5c0c28a | 2014-09-25 15:32:21 +0300 | [diff] [blame] | 342 | |
Sujit Reddy Thumma | c6e79da | 2014-09-25 15:32:23 +0300 | [diff] [blame] | 343 | err = ufshcd_parse_clock_info(hba); |
| 344 | if (err) { |
| 345 | dev_err(&pdev->dev, "%s: clock parse failed %d\n", |
| 346 | __func__, err); |
Yaniv Gardi | 47555a5 | 2015-10-28 13:15:49 +0200 | [diff] [blame] | 347 | goto dealloc_host; |
Sujit Reddy Thumma | c6e79da | 2014-09-25 15:32:23 +0300 | [diff] [blame] | 348 | } |
Sujit Reddy Thumma | aa49761 | 2014-09-25 15:32:22 +0300 | [diff] [blame] | 349 | err = ufshcd_parse_regulator_info(hba); |
| 350 | if (err) { |
| 351 | dev_err(&pdev->dev, "%s: regulator init failed %d\n", |
| 352 | __func__, err); |
Yaniv Gardi | 47555a5 | 2015-10-28 13:15:49 +0200 | [diff] [blame] | 353 | goto dealloc_host; |
Sujit Reddy Thumma | aa49761 | 2014-09-25 15:32:22 +0300 | [diff] [blame] | 354 | } |
| 355 | |
Yaniv Gardi | 54b879b | 2016-03-10 17:37:05 +0200 | [diff] [blame] | 356 | ufshcd_init_lanes_per_dir(hba); |
| 357 | |
Sujit Reddy Thumma | 5c0c28a | 2014-09-25 15:32:21 +0300 | [diff] [blame] | 358 | err = ufshcd_init(hba, mmio_base, irq); |
Vinayak Holikatti | 03b1781 | 2013-02-26 18:04:45 +0530 | [diff] [blame] | 359 | if (err) { |
Colin Ian King | bad9764 | 2015-11-28 16:33:56 +0000 | [diff] [blame] | 360 | dev_err(dev, "Initialization failed\n"); |
Stanley Chu | 24e2e7a1 | 2019-06-12 23:19:05 +0800 | [diff] [blame] | 361 | goto dealloc_host; |
Vinayak Holikatti | 03b1781 | 2013-02-26 18:04:45 +0530 | [diff] [blame] | 362 | } |
| 363 | |
| 364 | platform_set_drvdata(pdev, hba); |
| 365 | |
Stanley Chu | 24e2e7a1 | 2019-06-12 23:19:05 +0800 | [diff] [blame] | 366 | pm_runtime_set_active(&pdev->dev); |
| 367 | pm_runtime_enable(&pdev->dev); |
| 368 | |
Sujit Reddy Thumma | 6269473 | 2013-07-30 00:36:00 +0530 | [diff] [blame] | 369 | return 0; |
| 370 | |
Yaniv Gardi | 47555a5 | 2015-10-28 13:15:49 +0200 | [diff] [blame] | 371 | dealloc_host: |
| 372 | ufshcd_dealloc_host(hba); |
Seungwon Jeon | 2953f85 | 2013-06-27 13:31:54 +0900 | [diff] [blame] | 373 | out: |
Vinayak Holikatti | 03b1781 | 2013-02-26 18:04:45 +0530 | [diff] [blame] | 374 | return err; |
| 375 | } |
Yaniv Gardi | 47555a5 | 2015-10-28 13:15:49 +0200 | [diff] [blame] | 376 | EXPORT_SYMBOL_GPL(ufshcd_pltfrm_init); |
Vinayak Holikatti | 03b1781 | 2013-02-26 18:04:45 +0530 | [diff] [blame] | 377 | |
| 378 | MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>"); |
| 379 | MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>"); |
Joao Pinto | cc4959c | 2016-05-11 12:21:25 +0100 | [diff] [blame] | 380 | MODULE_DESCRIPTION("UFS host controller Platform bus based glue driver"); |
Vinayak Holikatti | 03b1781 | 2013-02-26 18:04:45 +0530 | [diff] [blame] | 381 | MODULE_LICENSE("GPL"); |
| 382 | MODULE_VERSION(UFSHCD_DRIVER_VERSION); |