| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Universal Flash Storage Host controller Platform bus based glue driver |
|---|
| 3 | | - * |
|---|
| 4 | | - * This code is based on drivers/scsi/ufs/ufshcd-pltfrm.c |
|---|
| 5 | 4 | * Copyright (C) 2011-2013 Samsung India Software Operations |
|---|
| 6 | 5 | * |
|---|
| 7 | 6 | * Authors: |
|---|
| 8 | 7 | * Santosh Yaraganavi <santosh.sy@samsung.com> |
|---|
| 9 | 8 | * Vinayak Holikatti <h.vinayak@samsung.com> |
|---|
| 10 | | - * |
|---|
| 11 | | - * This program is free software; you can redistribute it and/or |
|---|
| 12 | | - * modify it under the terms of the GNU General Public License |
|---|
| 13 | | - * as published by the Free Software Foundation; either version 2 |
|---|
| 14 | | - * of the License, or (at your option) any later version. |
|---|
| 15 | | - * See the COPYING file in the top-level directory or visit |
|---|
| 16 | | - * <http://www.gnu.org/licenses/gpl-2.0.html> |
|---|
| 17 | | - * |
|---|
| 18 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 19 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 20 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 21 | | - * GNU General Public License for more details. |
|---|
| 22 | | - * |
|---|
| 23 | | - * This program is provided "AS IS" and "WITH ALL FAULTS" and |
|---|
| 24 | | - * without warranty of any kind. You are solely responsible for |
|---|
| 25 | | - * determining the appropriateness of using and distributing |
|---|
| 26 | | - * the program and assume all risks associated with your exercise |
|---|
| 27 | | - * of rights with respect to the program, including but not limited |
|---|
| 28 | | - * to infringement of third party rights, the risks and costs of |
|---|
| 29 | | - * program errors, damage to or loss of data, programs or equipment, |
|---|
| 30 | | - * and unavailability or interruption of operations. Under no |
|---|
| 31 | | - * circumstances will the contributor of this Program be liable for |
|---|
| 32 | | - * any damages of any kind arising from your use or distribution of |
|---|
| 33 | | - * this program. |
|---|
| 34 | 9 | */ |
|---|
| 35 | 10 | |
|---|
| 36 | 11 | #include <linux/platform_device.h> |
|---|
| .. | .. |
|---|
| 39 | 14 | |
|---|
| 40 | 15 | #include "ufshcd.h" |
|---|
| 41 | 16 | #include "ufshcd-pltfrm.h" |
|---|
| 17 | +#include "unipro.h" |
|---|
| 42 | 18 | |
|---|
| 43 | 19 | #define UFSHCD_DEFAULT_LANES_PER_DIRECTION 2 |
|---|
| 44 | 20 | |
|---|
| .. | .. |
|---|
| 115 | 91 | |
|---|
| 116 | 92 | clki->min_freq = clkfreq[i]; |
|---|
| 117 | 93 | clki->max_freq = clkfreq[i+1]; |
|---|
| 118 | | - clki->name = kstrdup(name, GFP_KERNEL); |
|---|
| 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; |
|---|
| 119 | 102 | dev_dbg(dev, "%s: min %u max %u name %s\n", "freq-table-hz", |
|---|
| 120 | 103 | clki->min_freq, clki->max_freq, clki->name); |
|---|
| 121 | 104 | list_add_tail(&clki->list, &hba->clk_list_head); |
|---|
| .. | .. |
|---|
| 124 | 107 | return ret; |
|---|
| 125 | 108 | } |
|---|
| 126 | 109 | |
|---|
| 110 | +static 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 | + |
|---|
| 127 | 121 | #define MAX_PROP_SIZE 32 |
|---|
| 128 | 122 | static int ufshcd_populate_vreg(struct device *dev, const char *name, |
|---|
| 129 | | - struct ufs_vreg **out_vreg) |
|---|
| 123 | + struct ufs_vreg **out_vreg) |
|---|
| 130 | 124 | { |
|---|
| 131 | 125 | int ret = 0; |
|---|
| 132 | 126 | char prop_name[MAX_PROP_SIZE]; |
|---|
| .. | .. |
|---|
| 139 | 133 | } |
|---|
| 140 | 134 | |
|---|
| 141 | 135 | snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", name); |
|---|
| 142 | | - if (!of_parse_phandle(np, prop_name, 0)) { |
|---|
| 136 | + if (!phandle_exists(np, prop_name, 0)) { |
|---|
| 143 | 137 | dev_info(dev, "%s: Unable to find %s regulator, assuming enabled\n", |
|---|
| 144 | 138 | __func__, prop_name); |
|---|
| 145 | 139 | goto out; |
|---|
| .. | .. |
|---|
| 149 | 143 | if (!vreg) |
|---|
| 150 | 144 | return -ENOMEM; |
|---|
| 151 | 145 | |
|---|
| 152 | | - vreg->name = kstrdup(name, GFP_KERNEL); |
|---|
| 153 | | - |
|---|
| 154 | | - /* if fixed regulator no need further initialization */ |
|---|
| 155 | | - snprintf(prop_name, MAX_PROP_SIZE, "%s-fixed-regulator", name); |
|---|
| 156 | | - if (of_property_read_bool(np, prop_name)) |
|---|
| 157 | | - goto out; |
|---|
| 146 | + vreg->name = devm_kstrdup(dev, name, GFP_KERNEL); |
|---|
| 147 | + if (!vreg->name) |
|---|
| 148 | + return -ENOMEM; |
|---|
| 158 | 149 | |
|---|
| 159 | 150 | snprintf(prop_name, MAX_PROP_SIZE, "%s-max-microamp", name); |
|---|
| 160 | | - ret = of_property_read_u32(np, prop_name, &vreg->max_uA); |
|---|
| 161 | | - if (ret) { |
|---|
| 162 | | - dev_err(dev, "%s: unable to find %s err %d\n", |
|---|
| 163 | | - __func__, prop_name, ret); |
|---|
| 164 | | - goto out; |
|---|
| 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; |
|---|
| 165 | 154 | } |
|---|
| 166 | | - |
|---|
| 167 | | - vreg->min_uA = 0; |
|---|
| 168 | | - if (!strcmp(name, "vcc")) { |
|---|
| 169 | | - if (of_property_read_bool(np, "vcc-supply-1p8")) { |
|---|
| 170 | | - vreg->min_uV = UFS_VREG_VCC_1P8_MIN_UV; |
|---|
| 171 | | - vreg->max_uV = UFS_VREG_VCC_1P8_MAX_UV; |
|---|
| 172 | | - } else { |
|---|
| 173 | | - vreg->min_uV = UFS_VREG_VCC_MIN_UV; |
|---|
| 174 | | - vreg->max_uV = UFS_VREG_VCC_MAX_UV; |
|---|
| 175 | | - } |
|---|
| 176 | | - } else if (!strcmp(name, "vccq")) { |
|---|
| 177 | | - vreg->min_uV = UFS_VREG_VCCQ_MIN_UV; |
|---|
| 178 | | - vreg->max_uV = UFS_VREG_VCCQ_MAX_UV; |
|---|
| 179 | | - } else if (!strcmp(name, "vccq2")) { |
|---|
| 180 | | - vreg->min_uV = UFS_VREG_VCCQ2_MIN_UV; |
|---|
| 181 | | - vreg->max_uV = UFS_VREG_VCCQ2_MAX_UV; |
|---|
| 182 | | - } |
|---|
| 183 | | - |
|---|
| 184 | | - goto out; |
|---|
| 185 | | - |
|---|
| 186 | 155 | out: |
|---|
| 187 | 156 | if (!ret) |
|---|
| 188 | 157 | *out_vreg = vreg; |
|---|
| .. | .. |
|---|
| 290 | 259 | } |
|---|
| 291 | 260 | |
|---|
| 292 | 261 | /** |
|---|
| 262 | + * ufshcd_get_pwr_dev_param - get finally agreed attributes for |
|---|
| 263 | + * power mode change |
|---|
| 264 | + * @pltfrm_param: pointer to platform parameters |
|---|
| 265 | + * @dev_max: pointer to device attributes |
|---|
| 266 | + * @agreed_pwr: returned agreed attributes |
|---|
| 267 | + * |
|---|
| 268 | + * Returns 0 on success, non-zero value on failure |
|---|
| 269 | + */ |
|---|
| 270 | +int ufshcd_get_pwr_dev_param(struct ufs_dev_params *pltfrm_param, |
|---|
| 271 | + struct ufs_pa_layer_attr *dev_max, |
|---|
| 272 | + struct ufs_pa_layer_attr *agreed_pwr) |
|---|
| 273 | +{ |
|---|
| 274 | + int min_pltfrm_gear; |
|---|
| 275 | + int min_dev_gear; |
|---|
| 276 | + bool is_dev_sup_hs = false; |
|---|
| 277 | + bool is_pltfrm_max_hs = false; |
|---|
| 278 | + |
|---|
| 279 | + if (dev_max->pwr_rx == FAST_MODE) |
|---|
| 280 | + is_dev_sup_hs = true; |
|---|
| 281 | + |
|---|
| 282 | + if (pltfrm_param->desired_working_mode == UFS_HS_MODE) { |
|---|
| 283 | + is_pltfrm_max_hs = true; |
|---|
| 284 | + min_pltfrm_gear = min_t(u32, pltfrm_param->hs_rx_gear, |
|---|
| 285 | + pltfrm_param->hs_tx_gear); |
|---|
| 286 | + } else { |
|---|
| 287 | + min_pltfrm_gear = min_t(u32, pltfrm_param->pwm_rx_gear, |
|---|
| 288 | + pltfrm_param->pwm_tx_gear); |
|---|
| 289 | + } |
|---|
| 290 | + |
|---|
| 291 | + /* |
|---|
| 292 | + * device doesn't support HS but |
|---|
| 293 | + * pltfrm_param->desired_working_mode is HS, |
|---|
| 294 | + * thus device and pltfrm_param don't agree |
|---|
| 295 | + */ |
|---|
| 296 | + if (!is_dev_sup_hs && is_pltfrm_max_hs) { |
|---|
| 297 | + pr_info("%s: device doesn't support HS\n", |
|---|
| 298 | + __func__); |
|---|
| 299 | + return -ENOTSUPP; |
|---|
| 300 | + } else if (is_dev_sup_hs && is_pltfrm_max_hs) { |
|---|
| 301 | + /* |
|---|
| 302 | + * since device supports HS, it supports FAST_MODE. |
|---|
| 303 | + * since pltfrm_param->desired_working_mode is also HS |
|---|
| 304 | + * then final decision (FAST/FASTAUTO) is done according |
|---|
| 305 | + * to pltfrm_params as it is the restricting factor |
|---|
| 306 | + */ |
|---|
| 307 | + agreed_pwr->pwr_rx = pltfrm_param->rx_pwr_hs; |
|---|
| 308 | + agreed_pwr->pwr_tx = agreed_pwr->pwr_rx; |
|---|
| 309 | + } else { |
|---|
| 310 | + /* |
|---|
| 311 | + * here pltfrm_param->desired_working_mode is PWM. |
|---|
| 312 | + * it doesn't matter whether device supports HS or PWM, |
|---|
| 313 | + * in both cases pltfrm_param->desired_working_mode will |
|---|
| 314 | + * determine the mode |
|---|
| 315 | + */ |
|---|
| 316 | + agreed_pwr->pwr_rx = pltfrm_param->rx_pwr_pwm; |
|---|
| 317 | + agreed_pwr->pwr_tx = agreed_pwr->pwr_rx; |
|---|
| 318 | + } |
|---|
| 319 | + |
|---|
| 320 | + /* |
|---|
| 321 | + * we would like tx to work in the minimum number of lanes |
|---|
| 322 | + * between device capability and vendor preferences. |
|---|
| 323 | + * the same decision will be made for rx |
|---|
| 324 | + */ |
|---|
| 325 | + agreed_pwr->lane_tx = min_t(u32, dev_max->lane_tx, |
|---|
| 326 | + pltfrm_param->tx_lanes); |
|---|
| 327 | + agreed_pwr->lane_rx = min_t(u32, dev_max->lane_rx, |
|---|
| 328 | + pltfrm_param->rx_lanes); |
|---|
| 329 | + |
|---|
| 330 | + /* device maximum gear is the minimum between device rx and tx gears */ |
|---|
| 331 | + min_dev_gear = min_t(u32, dev_max->gear_rx, dev_max->gear_tx); |
|---|
| 332 | + |
|---|
| 333 | + /* |
|---|
| 334 | + * if both device capabilities and vendor pre-defined preferences are |
|---|
| 335 | + * both HS or both PWM then set the minimum gear to be the chosen |
|---|
| 336 | + * working gear. |
|---|
| 337 | + * if one is PWM and one is HS then the one that is PWM get to decide |
|---|
| 338 | + * what is the gear, as it is the one that also decided previously what |
|---|
| 339 | + * pwr the device will be configured to. |
|---|
| 340 | + */ |
|---|
| 341 | + if ((is_dev_sup_hs && is_pltfrm_max_hs) || |
|---|
| 342 | + (!is_dev_sup_hs && !is_pltfrm_max_hs)) { |
|---|
| 343 | + agreed_pwr->gear_rx = |
|---|
| 344 | + min_t(u32, min_dev_gear, min_pltfrm_gear); |
|---|
| 345 | + } else if (!is_dev_sup_hs) { |
|---|
| 346 | + agreed_pwr->gear_rx = min_dev_gear; |
|---|
| 347 | + } else { |
|---|
| 348 | + agreed_pwr->gear_rx = min_pltfrm_gear; |
|---|
| 349 | + } |
|---|
| 350 | + agreed_pwr->gear_tx = agreed_pwr->gear_rx; |
|---|
| 351 | + |
|---|
| 352 | + agreed_pwr->hs_rate = pltfrm_param->hs_rate; |
|---|
| 353 | + |
|---|
| 354 | + return 0; |
|---|
| 355 | +} |
|---|
| 356 | +EXPORT_SYMBOL_GPL(ufshcd_get_pwr_dev_param); |
|---|
| 357 | + |
|---|
| 358 | +/** |
|---|
| 293 | 359 | * ufshcd_pltfrm_init - probe routine of the driver |
|---|
| 294 | 360 | * @pdev: pointer to Platform device handle |
|---|
| 295 | 361 | * @vops: pointer to variant ops |
|---|
| .. | .. |
|---|
| 297 | 363 | * Returns 0 on success, non-zero value on failure |
|---|
| 298 | 364 | */ |
|---|
| 299 | 365 | int ufshcd_pltfrm_init(struct platform_device *pdev, |
|---|
| 300 | | - struct ufs_hba_variant_ops *vops) |
|---|
| 366 | + const struct ufs_hba_variant_ops *vops) |
|---|
| 301 | 367 | { |
|---|
| 302 | 368 | struct ufs_hba *hba; |
|---|
| 303 | 369 | void __iomem *mmio_base; |
|---|
| 304 | | - struct resource *mem_res; |
|---|
| 305 | 370 | int irq, err; |
|---|
| 306 | 371 | struct device *dev = &pdev->dev; |
|---|
| 307 | 372 | |
|---|
| 308 | | - mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| 309 | | - mmio_base = devm_ioremap_resource(dev, mem_res); |
|---|
| 373 | + mmio_base = devm_platform_ioremap_resource(pdev, 0); |
|---|
| 310 | 374 | if (IS_ERR(mmio_base)) { |
|---|
| 311 | 375 | err = PTR_ERR(mmio_base); |
|---|
| 312 | 376 | goto out; |
|---|
| .. | .. |
|---|
| 314 | 378 | |
|---|
| 315 | 379 | irq = platform_get_irq(pdev, 0); |
|---|
| 316 | 380 | if (irq < 0) { |
|---|
| 317 | | - dev_err(dev, "IRQ resource not available\n"); |
|---|
| 318 | | - err = -ENODEV; |
|---|
| 381 | + err = irq; |
|---|
| 319 | 382 | goto out; |
|---|
| 320 | 383 | } |
|---|
| 321 | 384 | |
|---|