471 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * phy-uniphier-usb3hs.c - HS-PHY driver for Socionext UniPhier USB3 controller4 * 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 0x027#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 0x437#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 450 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't155 * all-zero can be acquired. All-zero parameters mean nothing156 * 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");471