1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Universal Flash Storage Host controller Platform bus based glue driver 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> 9 */ 10 11#include <linux/platform_device.h> 12#include <linux/pm_runtime.h> 13#include <linux/of.h> 14 15#include "ufshcd.h" 16#include "ufshcd-pltfrm.h" 17#include "unipro.h" 18 19#define UFSHCD_DEFAULT_LANES_PER_DIRECTION 2 20 21static 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; 31 int len = 0; 32 size_t sz = 0; 33 34 if (!np) 35 goto out; 36 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 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 65 clkfreq = devm_kcalloc(dev, sz, sizeof(*clkfreq), 66 GFP_KERNEL); 67 if (!clkfreq) { 68 ret = -ENOMEM; 69 goto out; 70 } 71 72 ret = of_property_read_u32_array(np, "freq-table-hz", 73 clkfreq, sz); 74 if (ret && (ret != -EINVAL)) { 75 dev_err(dev, "%s: error reading array %d\n", 76 "freq-table-hz", ret); 77 return ret; 78 } 79 80 for (i = 0; i < sz; i += 2) { 81 ret = of_property_read_string_index(np, 82 "clock-names", i/2, (const char **)&name); 83 if (ret) 84 goto out; 85 86 clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL); 87 if (!clki) { 88 ret = -ENOMEM; 89 goto out; 90 } 91 92 clki->min_freq = clkfreq[i]; 93 clki->max_freq = clkfreq[i+1]; 94 clki->name = devm_kstrdup(dev, name, GFP_KERNEL); 95 if (!clki->name) { 96 ret = -ENOMEM; 97 goto out; 98 } 99 100 if (!strcmp(name, "ref_clk")) 101 clki->keep_link_active = true; 102 dev_dbg(dev, "%s: min %u max %u name %s\n", "freq-table-hz", 103 clki->min_freq, clki->max_freq, clki->name); 104 list_add_tail(&clki->list, &hba->clk_list_head); 105 } 106out: 107 return ret; 108} 109 110static bool phandle_exists(const struct device_node *np, 111 const char *phandle_name, int index) 112{ 113 struct device_node *parse_np = of_parse_phandle(np, phandle_name, index); 114 115 if (parse_np) 116 of_node_put(parse_np); 117 118 return parse_np != NULL; 119} 120 121#define MAX_PROP_SIZE 32 122static int ufshcd_populate_vreg(struct device *dev, const char *name, 123 struct ufs_vreg **out_vreg) 124{ 125 int ret = 0; 126 char prop_name[MAX_PROP_SIZE]; 127 struct ufs_vreg *vreg = NULL; 128 struct device_node *np = dev->of_node; 129 130 if (!np) { 131 dev_err(dev, "%s: non DT initialization\n", __func__); 132 goto out; 133 } 134 135 snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", name); 136 if (!phandle_exists(np, prop_name, 0)) { 137 dev_info(dev, "%s: Unable to find %s regulator, assuming enabled\n", 138 __func__, prop_name); 139 goto out; 140 } 141 142 vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL); 143 if (!vreg) 144 return -ENOMEM; 145 146 vreg->name = devm_kstrdup(dev, name, GFP_KERNEL); 147 if (!vreg->name) 148 return -ENOMEM; 149 150 snprintf(prop_name, MAX_PROP_SIZE, "%s-max-microamp", name); 151 if (of_property_read_u32(np, prop_name, &vreg->max_uA)) { 152 dev_info(dev, "%s: unable to find %s\n", __func__, prop_name); 153 vreg->max_uA = 0; 154 } 155 156 if (!strcmp(name, "vcc")) { 157 if (of_property_read_bool(np, "vcc-supply-1p8")) { 158 vreg->min_uV = UFS_VREG_VCC_1P8_MIN_UV; 159 vreg->max_uV = UFS_VREG_VCC_1P8_MAX_UV; 160 } else { 161 vreg->min_uV = UFS_VREG_VCC_MIN_UV; 162 vreg->max_uV = UFS_VREG_VCC_MAX_UV; 163 } 164 } else if (!strcmp(name, "vccq")) { 165 vreg->min_uV = UFS_VREG_VCCQ_MIN_UV; 166 vreg->max_uV = UFS_VREG_VCCQ_MAX_UV; 167 } else if (!strcmp(name, "vccq2")) { 168 vreg->min_uV = UFS_VREG_VCCQ2_MIN_UV; 169 vreg->max_uV = UFS_VREG_VCCQ2_MAX_UV; 170 } 171 172 goto out; 173 174out: 175 if (!ret) 176 *out_vreg = vreg; 177 return ret; 178} 179 180/** 181 * ufshcd_parse_regulator_info - get regulator info from device tree 182 * @hba: per adapter instance 183 * 184 * Get regulator info from device tree for vcc, vccq, vccq2 power supplies. 185 * If any of the supplies are not defined it is assumed that they are always-on 186 * and hence return zero. If the property is defined but parsing is failed 187 * then return corresponding error. 188 */ 189static int ufshcd_parse_regulator_info(struct ufs_hba *hba) 190{ 191 int err; 192 struct device *dev = hba->dev; 193 struct ufs_vreg_info *info = &hba->vreg_info; 194 195 err = ufshcd_populate_vreg(dev, "vdd-hba", &info->vdd_hba); 196 if (err) 197 goto out; 198 199 err = ufshcd_populate_vreg(dev, "vcc", &info->vcc); 200 if (err) 201 goto out; 202 203 err = ufshcd_populate_vreg(dev, "vccq", &info->vccq); 204 if (err) 205 goto out; 206 207 err = ufshcd_populate_vreg(dev, "vccq2", &info->vccq2); 208out: 209 return err; 210} 211 212#ifdef CONFIG_PM 213/** 214 * ufshcd_pltfrm_suspend - suspend power management function 215 * @dev: pointer to device handle 216 * 217 * Returns 0 if successful 218 * Returns non-zero otherwise 219 */ 220int ufshcd_pltfrm_suspend(struct device *dev) 221{ 222 return ufshcd_system_suspend(dev_get_drvdata(dev)); 223} 224EXPORT_SYMBOL_GPL(ufshcd_pltfrm_suspend); 225 226/** 227 * ufshcd_pltfrm_resume - resume power management function 228 * @dev: pointer to device handle 229 * 230 * Returns 0 if successful 231 * Returns non-zero otherwise 232 */ 233int ufshcd_pltfrm_resume(struct device *dev) 234{ 235 return ufshcd_system_resume(dev_get_drvdata(dev)); 236} 237EXPORT_SYMBOL_GPL(ufshcd_pltfrm_resume); 238 239int ufshcd_pltfrm_runtime_suspend(struct device *dev) 240{ 241 return ufshcd_runtime_suspend(dev_get_drvdata(dev)); 242} 243EXPORT_SYMBOL_GPL(ufshcd_pltfrm_runtime_suspend); 244 245int ufshcd_pltfrm_runtime_resume(struct device *dev) 246{ 247 return ufshcd_runtime_resume(dev_get_drvdata(dev)); 248} 249EXPORT_SYMBOL_GPL(ufshcd_pltfrm_runtime_resume); 250 251int ufshcd_pltfrm_runtime_idle(struct device *dev) 252{ 253 return ufshcd_runtime_idle(dev_get_drvdata(dev)); 254} 255EXPORT_SYMBOL_GPL(ufshcd_pltfrm_runtime_idle); 256 257#endif /* CONFIG_PM */ 258 259void ufshcd_pltfrm_shutdown(struct platform_device *pdev) 260{ 261 ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev)); 262} 263EXPORT_SYMBOL_GPL(ufshcd_pltfrm_shutdown); 264 265static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba) 266{ 267 struct device *dev = hba->dev; 268 int ret; 269 270 ret = of_property_read_u32(dev->of_node, "lanes-per-direction", 271 &hba->lanes_per_direction); 272 if (ret) { 273 dev_dbg(hba->dev, 274 "%s: failed to read lanes-per-direction, ret=%d\n", 275 __func__, ret); 276 hba->lanes_per_direction = UFSHCD_DEFAULT_LANES_PER_DIRECTION; 277 } 278} 279 280/** 281 * ufshcd_get_pwr_dev_param - get finally agreed attributes for 282 * power mode change 283 * @pltfrm_param: pointer to platform parameters 284 * @dev_max: pointer to device attributes 285 * @agreed_pwr: returned agreed attributes 286 * 287 * Returns 0 on success, non-zero value on failure 288 */ 289int ufshcd_get_pwr_dev_param(struct ufs_dev_params *pltfrm_param, 290 struct ufs_pa_layer_attr *dev_max, 291 struct ufs_pa_layer_attr *agreed_pwr) 292{ 293 int min_pltfrm_gear; 294 int min_dev_gear; 295 bool is_dev_sup_hs = false; 296 bool is_pltfrm_max_hs = false; 297 298 if (dev_max->pwr_rx == FAST_MODE) 299 is_dev_sup_hs = true; 300 301 if (pltfrm_param->desired_working_mode == UFS_HS_MODE) { 302 is_pltfrm_max_hs = true; 303 min_pltfrm_gear = min_t(u32, pltfrm_param->hs_rx_gear, 304 pltfrm_param->hs_tx_gear); 305 } else { 306 min_pltfrm_gear = min_t(u32, pltfrm_param->pwm_rx_gear, 307 pltfrm_param->pwm_tx_gear); 308 } 309 310 /* 311 * device doesn't support HS but 312 * pltfrm_param->desired_working_mode is HS, 313 * thus device and pltfrm_param don't agree 314 */ 315 if (!is_dev_sup_hs && is_pltfrm_max_hs) { 316 pr_info("%s: device doesn't support HS\n", 317 __func__); 318 return -ENOTSUPP; 319 } else if (is_dev_sup_hs && is_pltfrm_max_hs) { 320 /* 321 * since device supports HS, it supports FAST_MODE. 322 * since pltfrm_param->desired_working_mode is also HS 323 * then final decision (FAST/FASTAUTO) is done according 324 * to pltfrm_params as it is the restricting factor 325 */ 326 agreed_pwr->pwr_rx = pltfrm_param->rx_pwr_hs; 327 agreed_pwr->pwr_tx = agreed_pwr->pwr_rx; 328 } else { 329 /* 330 * here pltfrm_param->desired_working_mode is PWM. 331 * it doesn't matter whether device supports HS or PWM, 332 * in both cases pltfrm_param->desired_working_mode will 333 * determine the mode 334 */ 335 agreed_pwr->pwr_rx = pltfrm_param->rx_pwr_pwm; 336 agreed_pwr->pwr_tx = agreed_pwr->pwr_rx; 337 } 338 339 /* 340 * we would like tx to work in the minimum number of lanes 341 * between device capability and vendor preferences. 342 * the same decision will be made for rx 343 */ 344 agreed_pwr->lane_tx = min_t(u32, dev_max->lane_tx, 345 pltfrm_param->tx_lanes); 346 agreed_pwr->lane_rx = min_t(u32, dev_max->lane_rx, 347 pltfrm_param->rx_lanes); 348 349 /* device maximum gear is the minimum between device rx and tx gears */ 350 min_dev_gear = min_t(u32, dev_max->gear_rx, dev_max->gear_tx); 351 352 /* 353 * if both device capabilities and vendor pre-defined preferences are 354 * both HS or both PWM then set the minimum gear to be the chosen 355 * working gear. 356 * if one is PWM and one is HS then the one that is PWM get to decide 357 * what is the gear, as it is the one that also decided previously what 358 * pwr the device will be configured to. 359 */ 360 if ((is_dev_sup_hs && is_pltfrm_max_hs) || 361 (!is_dev_sup_hs && !is_pltfrm_max_hs)) { 362 agreed_pwr->gear_rx = 363 min_t(u32, min_dev_gear, min_pltfrm_gear); 364 } else if (!is_dev_sup_hs) { 365 agreed_pwr->gear_rx = min_dev_gear; 366 } else { 367 agreed_pwr->gear_rx = min_pltfrm_gear; 368 } 369 agreed_pwr->gear_tx = agreed_pwr->gear_rx; 370 371 agreed_pwr->hs_rate = pltfrm_param->hs_rate; 372 373 return 0; 374} 375EXPORT_SYMBOL_GPL(ufshcd_get_pwr_dev_param); 376 377/** 378 * ufshcd_pltfrm_init - probe routine of the driver 379 * @pdev: pointer to Platform device handle 380 * @vops: pointer to variant ops 381 * 382 * Returns 0 on success, non-zero value on failure 383 */ 384int ufshcd_pltfrm_init(struct platform_device *pdev, 385 const struct ufs_hba_variant_ops *vops) 386{ 387 struct ufs_hba *hba; 388 void __iomem *mmio_base; 389 int irq, err; 390 struct device *dev = &pdev->dev; 391 392 mmio_base = devm_platform_ioremap_resource(pdev, 0); 393 if (IS_ERR(mmio_base)) { 394 err = PTR_ERR(mmio_base); 395 goto out; 396 } 397 398 irq = platform_get_irq(pdev, 0); 399 if (irq < 0) { 400 err = irq; 401 goto out; 402 } 403 404 err = ufshcd_alloc_host(dev, &hba); 405 if (err) { 406 dev_err(&pdev->dev, "Allocation failed\n"); 407 goto out; 408 } 409 410 hba->vops = vops; 411 412 err = ufshcd_parse_clock_info(hba); 413 if (err) { 414 dev_err(&pdev->dev, "%s: clock parse failed %d\n", 415 __func__, err); 416 goto dealloc_host; 417 } 418 err = ufshcd_parse_regulator_info(hba); 419 if (err) { 420 dev_err(&pdev->dev, "%s: regulator init failed %d\n", 421 __func__, err); 422 goto dealloc_host; 423 } 424 425 ufshcd_init_lanes_per_dir(hba); 426 427 err = ufshcd_init(hba, mmio_base, irq); 428 if (err) { 429 dev_err(dev, "Initialization failed\n"); 430 goto dealloc_host; 431 } 432 433 pm_runtime_set_active(&pdev->dev); 434 pm_runtime_enable(&pdev->dev); 435 436 return 0; 437 438dealloc_host: 439 ufshcd_dealloc_host(hba); 440out: 441 return err; 442} 443EXPORT_SYMBOL_GPL(ufshcd_pltfrm_init); 444 445MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>"); 446MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>"); 447MODULE_DESCRIPTION("UFS host controller Platform bus based glue driver"); 448MODULE_LICENSE("GPL"); 449MODULE_VERSION(UFSHCD_DRIVER_VERSION); 450