| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Rockchip emmc PHY driver |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2016 Shawn Lin <shawn.lin@rock-chips.com> |
|---|
| 5 | 6 | * Copyright (C) 2016 ROCKCHIP, Inc. |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 8 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 9 | | - * the Free Software Foundation; either version 2 of the License. |
|---|
| 10 | | - * |
|---|
| 11 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 12 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 13 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 14 | | - * GNU General Public License for more details. |
|---|
| 15 | 7 | */ |
|---|
| 16 | 8 | |
|---|
| 17 | 9 | #include <linux/clk.h> |
|---|
| .. | .. |
|---|
| 87 | 79 | unsigned int reg_offset; |
|---|
| 88 | 80 | struct regmap *reg_base; |
|---|
| 89 | 81 | struct clk *emmcclk; |
|---|
| 82 | + unsigned int drive_impedance; |
|---|
| 90 | 83 | }; |
|---|
| 91 | 84 | |
|---|
| 92 | 85 | static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) |
|---|
| .. | .. |
|---|
| 176 | 169 | ret = regmap_read_poll_timeout(rk_phy->reg_base, |
|---|
| 177 | 170 | rk_phy->reg_offset + GRF_EMMCPHY_STATUS, |
|---|
| 178 | 171 | caldone, PHYCTRL_IS_CALDONE(caldone), |
|---|
| 179 | | - 0, 500); |
|---|
| 172 | + 0, 50); |
|---|
| 180 | 173 | if (ret) { |
|---|
| 181 | 174 | pr_err("%s: caldone failed, ret=%d\n", __func__, ret); |
|---|
| 182 | 175 | return ret; |
|---|
| .. | .. |
|---|
| 247 | 240 | * - SDHCI driver to get the PHY |
|---|
| 248 | 241 | * - SDHCI driver to init the PHY |
|---|
| 249 | 242 | * |
|---|
| 250 | | - * The clock is optional, so upon any error we just set to NULL. |
|---|
| 243 | + * The clock is optional, using clk_get_optional() to get the clock |
|---|
| 244 | + * and do error processing if the return value != NULL |
|---|
| 251 | 245 | * |
|---|
| 252 | 246 | * NOTE: we don't do anything special for EPROBE_DEFER here. Given the |
|---|
| 253 | 247 | * above expected use case, EPROBE_DEFER isn't sensible to expect, so |
|---|
| 254 | 248 | * it's just like any other error. |
|---|
| 255 | 249 | */ |
|---|
| 256 | | - rk_phy->emmcclk = clk_get(&phy->dev, "emmcclk"); |
|---|
| 250 | + rk_phy->emmcclk = clk_get_optional(&phy->dev, "emmcclk"); |
|---|
| 257 | 251 | if (IS_ERR(rk_phy->emmcclk)) { |
|---|
| 258 | | - dev_dbg(&phy->dev, "Error getting emmcclk: %d\n", ret); |
|---|
| 252 | + ret = PTR_ERR(rk_phy->emmcclk); |
|---|
| 253 | + dev_err(&phy->dev, "Error getting emmcclk: %d\n", ret); |
|---|
| 259 | 254 | rk_phy->emmcclk = NULL; |
|---|
| 260 | 255 | } |
|---|
| 261 | 256 | |
|---|
| .. | .. |
|---|
| 281 | 276 | { |
|---|
| 282 | 277 | struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); |
|---|
| 283 | 278 | |
|---|
| 284 | | - /* Drive impedance: 50 Ohm */ |
|---|
| 279 | + /* Drive impedance: from DTS */ |
|---|
| 285 | 280 | regmap_write(rk_phy->reg_base, |
|---|
| 286 | 281 | rk_phy->reg_offset + GRF_EMMCPHY_CON6, |
|---|
| 287 | | - HIWORD_UPDATE(PHYCTRL_DR_50OHM, |
|---|
| 282 | + HIWORD_UPDATE(rk_phy->drive_impedance, |
|---|
| 288 | 283 | PHYCTRL_DR_MASK, |
|---|
| 289 | 284 | PHYCTRL_DR_SHIFT)); |
|---|
| 290 | 285 | |
|---|
| .. | .. |
|---|
| 314 | 309 | .owner = THIS_MODULE, |
|---|
| 315 | 310 | }; |
|---|
| 316 | 311 | |
|---|
| 312 | +static u32 convert_drive_impedance_ohm(struct platform_device *pdev, u32 dr_ohm) |
|---|
| 313 | +{ |
|---|
| 314 | + switch (dr_ohm) { |
|---|
| 315 | + case 100: |
|---|
| 316 | + return PHYCTRL_DR_100OHM; |
|---|
| 317 | + case 66: |
|---|
| 318 | + return PHYCTRL_DR_66OHM; |
|---|
| 319 | + case 50: |
|---|
| 320 | + return PHYCTRL_DR_50OHM; |
|---|
| 321 | + case 40: |
|---|
| 322 | + return PHYCTRL_DR_40OHM; |
|---|
| 323 | + case 33: |
|---|
| 324 | + return PHYCTRL_DR_33OHM; |
|---|
| 325 | + } |
|---|
| 326 | + |
|---|
| 327 | + dev_warn(&pdev->dev, "Invalid value %u for drive-impedance-ohm.\n", |
|---|
| 328 | + dr_ohm); |
|---|
| 329 | + return PHYCTRL_DR_50OHM; |
|---|
| 330 | +} |
|---|
| 331 | + |
|---|
| 317 | 332 | static int rockchip_emmc_phy_probe(struct platform_device *pdev) |
|---|
| 318 | 333 | { |
|---|
| 319 | 334 | struct device *dev = &pdev->dev; |
|---|
| .. | .. |
|---|
| 322 | 337 | struct phy_provider *phy_provider; |
|---|
| 323 | 338 | struct regmap *grf; |
|---|
| 324 | 339 | unsigned int reg_offset; |
|---|
| 340 | + u32 val; |
|---|
| 325 | 341 | |
|---|
| 326 | 342 | if (!dev->parent || !dev->parent->of_node) |
|---|
| 327 | 343 | return -ENODEV; |
|---|
| .. | .. |
|---|
| 337 | 353 | return -ENOMEM; |
|---|
| 338 | 354 | |
|---|
| 339 | 355 | if (of_property_read_u32(dev->of_node, "reg", ®_offset)) { |
|---|
| 340 | | - dev_err(dev, "missing reg property in node %s\n", |
|---|
| 341 | | - dev->of_node->name); |
|---|
| 356 | + dev_err(dev, "missing reg property in node %pOFn\n", |
|---|
| 357 | + dev->of_node); |
|---|
| 342 | 358 | return -EINVAL; |
|---|
| 343 | 359 | } |
|---|
| 344 | 360 | |
|---|
| 345 | 361 | rk_phy->reg_offset = reg_offset; |
|---|
| 346 | 362 | rk_phy->reg_base = grf; |
|---|
| 363 | + rk_phy->drive_impedance = PHYCTRL_DR_50OHM; |
|---|
| 364 | + |
|---|
| 365 | + if (!of_property_read_u32(dev->of_node, "drive-impedance-ohm", &val)) |
|---|
| 366 | + rk_phy->drive_impedance = convert_drive_impedance_ohm(pdev, val); |
|---|
| 347 | 367 | |
|---|
| 348 | 368 | generic_phy = devm_phy_create(dev, dev->of_node, &ops); |
|---|
| 349 | 369 | if (IS_ERR(generic_phy)) { |
|---|