phy-uniphier-usb3hs.c (11639B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * phy-uniphier-usb3hs.c - HS-PHY driver for Socionext UniPhier USB3 controller 4 * Copyright 2015-2018 Socionext Inc. 5 * Author: 6 * Kunihiko Hayashi <hayashi.kunihiko@socionext.com> 7 * Contributors: 8 * Motoya Tanigawa <tanigawa.motoya@socionext.com> 9 * Masami Hiramatsu <masami.hiramatsu@linaro.org> 10 */ 11 12#include <linux/bitfield.h> 13#include <linux/bitops.h> 14#include <linux/clk.h> 15#include <linux/io.h> 16#include <linux/module.h> 17#include <linux/nvmem-consumer.h> 18#include <linux/of.h> 19#include <linux/of_platform.h> 20#include <linux/phy/phy.h> 21#include <linux/platform_device.h> 22#include <linux/regulator/consumer.h> 23#include <linux/reset.h> 24#include <linux/slab.h> 25 26#define HSPHY_CFG0 0x0 27#define HSPHY_CFG0_HS_I_MASK GENMASK(31, 28) 28#define HSPHY_CFG0_HSDISC_MASK GENMASK(27, 26) 29#define HSPHY_CFG0_SWING_MASK GENMASK(17, 16) 30#define HSPHY_CFG0_SEL_T_MASK GENMASK(15, 12) 31#define HSPHY_CFG0_RTERM_MASK GENMASK(7, 6) 32#define HSPHY_CFG0_TRIMMASK (HSPHY_CFG0_HS_I_MASK \ 33 | HSPHY_CFG0_SEL_T_MASK \ 34 | HSPHY_CFG0_RTERM_MASK) 35 36#define HSPHY_CFG1 0x4 37#define HSPHY_CFG1_DAT_EN BIT(29) 38#define HSPHY_CFG1_ADR_EN BIT(28) 39#define HSPHY_CFG1_ADR_MASK GENMASK(27, 16) 40#define HSPHY_CFG1_DAT_MASK GENMASK(23, 16) 41 42#define PHY_F(regno, msb, lsb) { (regno), (msb), (lsb) } 43 44#define RX_CHK_SYNC PHY_F(0, 5, 5) /* RX sync mode */ 45#define RX_SYNC_SEL PHY_F(1, 1, 0) /* RX sync length */ 46#define LS_SLEW PHY_F(10, 6, 6) /* LS mode slew rate */ 47#define FS_LS_DRV PHY_F(10, 5, 5) /* FS/LS slew rate */ 48 49#define MAX_PHY_PARAMS 4 50 51struct uniphier_u3hsphy_param { 52 struct { 53 int reg_no; 54 int msb; 55 int lsb; 56 } field; 57 u8 value; 58}; 59 60struct uniphier_u3hsphy_trim_param { 61 unsigned int rterm; 62 unsigned int sel_t; 63 unsigned int hs_i; 64}; 65 66#define trim_param_is_valid(p) ((p)->rterm || (p)->sel_t || (p)->hs_i) 67 68struct uniphier_u3hsphy_priv { 69 struct device *dev; 70 void __iomem *base; 71 struct clk *clk, *clk_parent, *clk_ext, *clk_parent_gio; 72 struct reset_control *rst, *rst_parent, *rst_parent_gio; 73 struct regulator *vbus; 74 const struct uniphier_u3hsphy_soc_data *data; 75}; 76 77struct uniphier_u3hsphy_soc_data { 78 bool is_legacy; 79 int nparams; 80 const struct uniphier_u3hsphy_param param[MAX_PHY_PARAMS]; 81 u32 config0; 82 u32 config1; 83 void (*trim_func)(struct uniphier_u3hsphy_priv *priv, u32 *pconfig, 84 struct uniphier_u3hsphy_trim_param *pt); 85}; 86 87static void uniphier_u3hsphy_trim_ld20(struct uniphier_u3hsphy_priv *priv, 88 u32 *pconfig, 89 struct uniphier_u3hsphy_trim_param *pt) 90{ 91 *pconfig &= ~HSPHY_CFG0_RTERM_MASK; 92 *pconfig |= FIELD_PREP(HSPHY_CFG0_RTERM_MASK, pt->rterm); 93 94 *pconfig &= ~HSPHY_CFG0_SEL_T_MASK; 95 *pconfig |= FIELD_PREP(HSPHY_CFG0_SEL_T_MASK, pt->sel_t); 96 97 *pconfig &= ~HSPHY_CFG0_HS_I_MASK; 98 *pconfig |= FIELD_PREP(HSPHY_CFG0_HS_I_MASK, pt->hs_i); 99} 100 101static int uniphier_u3hsphy_get_nvparam(struct uniphier_u3hsphy_priv *priv, 102 const char *name, unsigned int *val) 103{ 104 struct nvmem_cell *cell; 105 u8 *buf; 106 107 cell = devm_nvmem_cell_get(priv->dev, name); 108 if (IS_ERR(cell)) 109 return PTR_ERR(cell); 110 111 buf = nvmem_cell_read(cell, NULL); 112 if (IS_ERR(buf)) 113 return PTR_ERR(buf); 114 115 *val = *buf; 116 117 kfree(buf); 118 119 return 0; 120} 121 122static int uniphier_u3hsphy_get_nvparams(struct uniphier_u3hsphy_priv *priv, 123 struct uniphier_u3hsphy_trim_param *pt) 124{ 125 int ret; 126 127 ret = uniphier_u3hsphy_get_nvparam(priv, "rterm", &pt->rterm); 128 if (ret) 129 return ret; 130 131 ret = uniphier_u3hsphy_get_nvparam(priv, "sel_t", &pt->sel_t); 132 if (ret) 133 return ret; 134 135 ret = uniphier_u3hsphy_get_nvparam(priv, "hs_i", &pt->hs_i); 136 if (ret) 137 return ret; 138 139 return 0; 140} 141 142static int uniphier_u3hsphy_update_config(struct uniphier_u3hsphy_priv *priv, 143 u32 *pconfig) 144{ 145 struct uniphier_u3hsphy_trim_param trim; 146 int ret, trimmed = 0; 147 148 if (priv->data->trim_func) { 149 ret = uniphier_u3hsphy_get_nvparams(priv, &trim); 150 if (ret == -EPROBE_DEFER) 151 return ret; 152 153 /* 154 * call trim_func only when trimming parameters that aren't 155 * all-zero can be acquired. All-zero parameters mean nothing 156 * has been written to nvmem. 157 */ 158 if (!ret && trim_param_is_valid(&trim)) { 159 priv->data->trim_func(priv, pconfig, &trim); 160 trimmed = 1; 161 } else { 162 dev_dbg(priv->dev, "can't get parameter from nvmem\n"); 163 } 164 } 165 166 /* use default parameters without trimming values */ 167 if (!trimmed) { 168 *pconfig &= ~HSPHY_CFG0_HSDISC_MASK; 169 *pconfig |= FIELD_PREP(HSPHY_CFG0_HSDISC_MASK, 3); 170 } 171 172 return 0; 173} 174 175static void uniphier_u3hsphy_set_param(struct uniphier_u3hsphy_priv *priv, 176 const struct uniphier_u3hsphy_param *p) 177{ 178 u32 val; 179 u32 field_mask = GENMASK(p->field.msb, p->field.lsb); 180 u8 data; 181 182 val = readl(priv->base + HSPHY_CFG1); 183 val &= ~HSPHY_CFG1_ADR_MASK; 184 val |= FIELD_PREP(HSPHY_CFG1_ADR_MASK, p->field.reg_no) 185 | HSPHY_CFG1_ADR_EN; 186 writel(val, priv->base + HSPHY_CFG1); 187 188 val = readl(priv->base + HSPHY_CFG1); 189 val &= ~HSPHY_CFG1_ADR_EN; 190 writel(val, priv->base + HSPHY_CFG1); 191 192 val = readl(priv->base + HSPHY_CFG1); 193 val &= ~FIELD_PREP(HSPHY_CFG1_DAT_MASK, field_mask); 194 data = field_mask & (p->value << p->field.lsb); 195 val |= FIELD_PREP(HSPHY_CFG1_DAT_MASK, data) | HSPHY_CFG1_DAT_EN; 196 writel(val, priv->base + HSPHY_CFG1); 197 198 val = readl(priv->base + HSPHY_CFG1); 199 val &= ~HSPHY_CFG1_DAT_EN; 200 writel(val, priv->base + HSPHY_CFG1); 201} 202 203static int uniphier_u3hsphy_power_on(struct phy *phy) 204{ 205 struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy); 206 int ret; 207 208 ret = clk_prepare_enable(priv->clk_ext); 209 if (ret) 210 return ret; 211 212 ret = clk_prepare_enable(priv->clk); 213 if (ret) 214 goto out_clk_ext_disable; 215 216 ret = reset_control_deassert(priv->rst); 217 if (ret) 218 goto out_clk_disable; 219 220 if (priv->vbus) { 221 ret = regulator_enable(priv->vbus); 222 if (ret) 223 goto out_rst_assert; 224 } 225 226 return 0; 227 228out_rst_assert: 229 reset_control_assert(priv->rst); 230out_clk_disable: 231 clk_disable_unprepare(priv->clk); 232out_clk_ext_disable: 233 clk_disable_unprepare(priv->clk_ext); 234 235 return ret; 236} 237 238static int uniphier_u3hsphy_power_off(struct phy *phy) 239{ 240 struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy); 241 242 if (priv->vbus) 243 regulator_disable(priv->vbus); 244 245 reset_control_assert(priv->rst); 246 clk_disable_unprepare(priv->clk); 247 clk_disable_unprepare(priv->clk_ext); 248 249 return 0; 250} 251 252static int uniphier_u3hsphy_init(struct phy *phy) 253{ 254 struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy); 255 u32 config0, config1; 256 int i, ret; 257 258 ret = clk_prepare_enable(priv->clk_parent); 259 if (ret) 260 return ret; 261 262 ret = clk_prepare_enable(priv->clk_parent_gio); 263 if (ret) 264 goto out_clk_disable; 265 266 ret = reset_control_deassert(priv->rst_parent); 267 if (ret) 268 goto out_clk_gio_disable; 269 270 ret = reset_control_deassert(priv->rst_parent_gio); 271 if (ret) 272 goto out_rst_assert; 273 274 if ((priv->data->is_legacy) 275 || (!priv->data->config0 && !priv->data->config1)) 276 return 0; 277 278 config0 = priv->data->config0; 279 config1 = priv->data->config1; 280 281 ret = uniphier_u3hsphy_update_config(priv, &config0); 282 if (ret) 283 goto out_rst_assert; 284 285 writel(config0, priv->base + HSPHY_CFG0); 286 writel(config1, priv->base + HSPHY_CFG1); 287 288 for (i = 0; i < priv->data->nparams; i++) 289 uniphier_u3hsphy_set_param(priv, &priv->data->param[i]); 290 291 return 0; 292 293out_rst_assert: 294 reset_control_assert(priv->rst_parent); 295out_clk_gio_disable: 296 clk_disable_unprepare(priv->clk_parent_gio); 297out_clk_disable: 298 clk_disable_unprepare(priv->clk_parent); 299 300 return ret; 301} 302 303static int uniphier_u3hsphy_exit(struct phy *phy) 304{ 305 struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy); 306 307 reset_control_assert(priv->rst_parent_gio); 308 reset_control_assert(priv->rst_parent); 309 clk_disable_unprepare(priv->clk_parent_gio); 310 clk_disable_unprepare(priv->clk_parent); 311 312 return 0; 313} 314 315static const struct phy_ops uniphier_u3hsphy_ops = { 316 .init = uniphier_u3hsphy_init, 317 .exit = uniphier_u3hsphy_exit, 318 .power_on = uniphier_u3hsphy_power_on, 319 .power_off = uniphier_u3hsphy_power_off, 320 .owner = THIS_MODULE, 321}; 322 323static int uniphier_u3hsphy_probe(struct platform_device *pdev) 324{ 325 struct device *dev = &pdev->dev; 326 struct uniphier_u3hsphy_priv *priv; 327 struct phy_provider *phy_provider; 328 struct phy *phy; 329 330 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 331 if (!priv) 332 return -ENOMEM; 333 334 priv->dev = dev; 335 priv->data = of_device_get_match_data(dev); 336 if (WARN_ON(!priv->data || 337 priv->data->nparams > MAX_PHY_PARAMS)) 338 return -EINVAL; 339 340 priv->base = devm_platform_ioremap_resource(pdev, 0); 341 if (IS_ERR(priv->base)) 342 return PTR_ERR(priv->base); 343 344 if (!priv->data->is_legacy) { 345 priv->clk = devm_clk_get(dev, "phy"); 346 if (IS_ERR(priv->clk)) 347 return PTR_ERR(priv->clk); 348 349 priv->clk_ext = devm_clk_get_optional(dev, "phy-ext"); 350 if (IS_ERR(priv->clk_ext)) 351 return PTR_ERR(priv->clk_ext); 352 353 priv->rst = devm_reset_control_get_shared(dev, "phy"); 354 if (IS_ERR(priv->rst)) 355 return PTR_ERR(priv->rst); 356 357 } else { 358 priv->clk_parent_gio = devm_clk_get(dev, "gio"); 359 if (IS_ERR(priv->clk_parent_gio)) 360 return PTR_ERR(priv->clk_parent_gio); 361 362 priv->rst_parent_gio = 363 devm_reset_control_get_shared(dev, "gio"); 364 if (IS_ERR(priv->rst_parent_gio)) 365 return PTR_ERR(priv->rst_parent_gio); 366 } 367 368 priv->clk_parent = devm_clk_get(dev, "link"); 369 if (IS_ERR(priv->clk_parent)) 370 return PTR_ERR(priv->clk_parent); 371 372 priv->rst_parent = devm_reset_control_get_shared(dev, "link"); 373 if (IS_ERR(priv->rst_parent)) 374 return PTR_ERR(priv->rst_parent); 375 376 priv->vbus = devm_regulator_get_optional(dev, "vbus"); 377 if (IS_ERR(priv->vbus)) { 378 if (PTR_ERR(priv->vbus) == -EPROBE_DEFER) 379 return PTR_ERR(priv->vbus); 380 priv->vbus = NULL; 381 } 382 383 phy = devm_phy_create(dev, dev->of_node, &uniphier_u3hsphy_ops); 384 if (IS_ERR(phy)) 385 return PTR_ERR(phy); 386 387 phy_set_drvdata(phy, priv); 388 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 389 390 return PTR_ERR_OR_ZERO(phy_provider); 391} 392 393static const struct uniphier_u3hsphy_soc_data uniphier_pro5_data = { 394 .is_legacy = true, 395 .nparams = 0, 396}; 397 398static const struct uniphier_u3hsphy_soc_data uniphier_pxs2_data = { 399 .is_legacy = false, 400 .nparams = 2, 401 .param = { 402 { RX_CHK_SYNC, 1 }, 403 { RX_SYNC_SEL, 1 }, 404 }, 405}; 406 407static const struct uniphier_u3hsphy_soc_data uniphier_ld20_data = { 408 .is_legacy = false, 409 .nparams = 4, 410 .param = { 411 { RX_CHK_SYNC, 1 }, 412 { RX_SYNC_SEL, 1 }, 413 { LS_SLEW, 1 }, 414 { FS_LS_DRV, 1 }, 415 }, 416 .trim_func = uniphier_u3hsphy_trim_ld20, 417 .config0 = 0x92316680, 418 .config1 = 0x00000106, 419}; 420 421static const struct uniphier_u3hsphy_soc_data uniphier_pxs3_data = { 422 .is_legacy = false, 423 .nparams = 2, 424 .param = { 425 { RX_CHK_SYNC, 1 }, 426 { RX_SYNC_SEL, 1 }, 427 }, 428 .trim_func = uniphier_u3hsphy_trim_ld20, 429 .config0 = 0x92316680, 430 .config1 = 0x00000106, 431}; 432 433static const struct of_device_id uniphier_u3hsphy_match[] = { 434 { 435 .compatible = "socionext,uniphier-pro5-usb3-hsphy", 436 .data = &uniphier_pro5_data, 437 }, 438 { 439 .compatible = "socionext,uniphier-pxs2-usb3-hsphy", 440 .data = &uniphier_pxs2_data, 441 }, 442 { 443 .compatible = "socionext,uniphier-ld20-usb3-hsphy", 444 .data = &uniphier_ld20_data, 445 }, 446 { 447 .compatible = "socionext,uniphier-pxs3-usb3-hsphy", 448 .data = &uniphier_pxs3_data, 449 }, 450 { 451 .compatible = "socionext,uniphier-nx1-usb3-hsphy", 452 .data = &uniphier_pxs3_data, 453 }, 454 { /* sentinel */ } 455}; 456MODULE_DEVICE_TABLE(of, uniphier_u3hsphy_match); 457 458static struct platform_driver uniphier_u3hsphy_driver = { 459 .probe = uniphier_u3hsphy_probe, 460 .driver = { 461 .name = "uniphier-usb3-hsphy", 462 .of_match_table = uniphier_u3hsphy_match, 463 }, 464}; 465 466module_platform_driver(uniphier_u3hsphy_driver); 467 468MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>"); 469MODULE_DESCRIPTION("UniPhier HS-PHY driver for USB3 controller"); 470MODULE_LICENSE("GPL v2");