phy-uniphier-usb3ss.c (8982B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * phy-uniphier-usb3ss.c - SS-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/of.h> 18#include <linux/of_platform.h> 19#include <linux/phy/phy.h> 20#include <linux/platform_device.h> 21#include <linux/regulator/consumer.h> 22#include <linux/reset.h> 23 24#define SSPHY_TESTI 0x0 25#define TESTI_DAT_MASK GENMASK(13, 6) 26#define TESTI_ADR_MASK GENMASK(5, 1) 27#define TESTI_WR_EN BIT(0) 28 29#define SSPHY_TESTO 0x4 30#define TESTO_DAT_MASK GENMASK(7, 0) 31 32#define PHY_F(regno, msb, lsb) { (regno), (msb), (lsb) } 33 34#define CDR_CPD_TRIM PHY_F(7, 3, 0) /* RxPLL charge pump current */ 35#define CDR_CPF_TRIM PHY_F(8, 3, 0) /* RxPLL charge pump current 2 */ 36#define TX_PLL_TRIM PHY_F(9, 3, 0) /* TxPLL charge pump current */ 37#define BGAP_TRIM PHY_F(11, 3, 0) /* Bandgap voltage */ 38#define CDR_TRIM PHY_F(13, 6, 5) /* Clock Data Recovery setting */ 39#define VCO_CTRL PHY_F(26, 7, 4) /* VCO control */ 40#define VCOPLL_CTRL PHY_F(27, 2, 0) /* TxPLL VCO tuning */ 41#define VCOPLL_CM PHY_F(28, 1, 0) /* TxPLL voltage */ 42 43#define MAX_PHY_PARAMS 7 44 45struct uniphier_u3ssphy_param { 46 struct { 47 int reg_no; 48 int msb; 49 int lsb; 50 } field; 51 u8 value; 52}; 53 54struct uniphier_u3ssphy_priv { 55 struct device *dev; 56 void __iomem *base; 57 struct clk *clk, *clk_ext, *clk_parent, *clk_parent_gio; 58 struct reset_control *rst, *rst_parent, *rst_parent_gio; 59 struct regulator *vbus; 60 const struct uniphier_u3ssphy_soc_data *data; 61}; 62 63struct uniphier_u3ssphy_soc_data { 64 bool is_legacy; 65 int nparams; 66 const struct uniphier_u3ssphy_param param[MAX_PHY_PARAMS]; 67}; 68 69static void uniphier_u3ssphy_testio_write(struct uniphier_u3ssphy_priv *priv, 70 u32 data) 71{ 72 /* need to read TESTO twice after accessing TESTI */ 73 writel(data, priv->base + SSPHY_TESTI); 74 readl(priv->base + SSPHY_TESTO); 75 readl(priv->base + SSPHY_TESTO); 76} 77 78static void uniphier_u3ssphy_set_param(struct uniphier_u3ssphy_priv *priv, 79 const struct uniphier_u3ssphy_param *p) 80{ 81 u32 val; 82 u8 field_mask = GENMASK(p->field.msb, p->field.lsb); 83 u8 data; 84 85 /* read previous data */ 86 val = FIELD_PREP(TESTI_DAT_MASK, 1); 87 val |= FIELD_PREP(TESTI_ADR_MASK, p->field.reg_no); 88 uniphier_u3ssphy_testio_write(priv, val); 89 val = readl(priv->base + SSPHY_TESTO) & TESTO_DAT_MASK; 90 91 /* update value */ 92 val &= ~field_mask; 93 data = field_mask & (p->value << p->field.lsb); 94 val = FIELD_PREP(TESTI_DAT_MASK, data | val); 95 val |= FIELD_PREP(TESTI_ADR_MASK, p->field.reg_no); 96 uniphier_u3ssphy_testio_write(priv, val); 97 uniphier_u3ssphy_testio_write(priv, val | TESTI_WR_EN); 98 uniphier_u3ssphy_testio_write(priv, val); 99 100 /* read current data as dummy */ 101 val = FIELD_PREP(TESTI_DAT_MASK, 1); 102 val |= FIELD_PREP(TESTI_ADR_MASK, p->field.reg_no); 103 uniphier_u3ssphy_testio_write(priv, val); 104 readl(priv->base + SSPHY_TESTO); 105} 106 107static int uniphier_u3ssphy_power_on(struct phy *phy) 108{ 109 struct uniphier_u3ssphy_priv *priv = phy_get_drvdata(phy); 110 int ret; 111 112 ret = clk_prepare_enable(priv->clk_ext); 113 if (ret) 114 return ret; 115 116 ret = clk_prepare_enable(priv->clk); 117 if (ret) 118 goto out_clk_ext_disable; 119 120 ret = reset_control_deassert(priv->rst); 121 if (ret) 122 goto out_clk_disable; 123 124 if (priv->vbus) { 125 ret = regulator_enable(priv->vbus); 126 if (ret) 127 goto out_rst_assert; 128 } 129 130 return 0; 131 132out_rst_assert: 133 reset_control_assert(priv->rst); 134out_clk_disable: 135 clk_disable_unprepare(priv->clk); 136out_clk_ext_disable: 137 clk_disable_unprepare(priv->clk_ext); 138 139 return ret; 140} 141 142static int uniphier_u3ssphy_power_off(struct phy *phy) 143{ 144 struct uniphier_u3ssphy_priv *priv = phy_get_drvdata(phy); 145 146 if (priv->vbus) 147 regulator_disable(priv->vbus); 148 149 reset_control_assert(priv->rst); 150 clk_disable_unprepare(priv->clk); 151 clk_disable_unprepare(priv->clk_ext); 152 153 return 0; 154} 155 156static int uniphier_u3ssphy_init(struct phy *phy) 157{ 158 struct uniphier_u3ssphy_priv *priv = phy_get_drvdata(phy); 159 int i, ret; 160 161 ret = clk_prepare_enable(priv->clk_parent); 162 if (ret) 163 return ret; 164 165 ret = clk_prepare_enable(priv->clk_parent_gio); 166 if (ret) 167 goto out_clk_disable; 168 169 ret = reset_control_deassert(priv->rst_parent); 170 if (ret) 171 goto out_clk_gio_disable; 172 173 ret = reset_control_deassert(priv->rst_parent_gio); 174 if (ret) 175 goto out_rst_assert; 176 177 if (priv->data->is_legacy) 178 return 0; 179 180 for (i = 0; i < priv->data->nparams; i++) 181 uniphier_u3ssphy_set_param(priv, &priv->data->param[i]); 182 183 return 0; 184 185out_rst_assert: 186 reset_control_assert(priv->rst_parent); 187out_clk_gio_disable: 188 clk_disable_unprepare(priv->clk_parent_gio); 189out_clk_disable: 190 clk_disable_unprepare(priv->clk_parent); 191 192 return ret; 193} 194 195static int uniphier_u3ssphy_exit(struct phy *phy) 196{ 197 struct uniphier_u3ssphy_priv *priv = phy_get_drvdata(phy); 198 199 reset_control_assert(priv->rst_parent_gio); 200 reset_control_assert(priv->rst_parent); 201 clk_disable_unprepare(priv->clk_parent_gio); 202 clk_disable_unprepare(priv->clk_parent); 203 204 return 0; 205} 206 207static const struct phy_ops uniphier_u3ssphy_ops = { 208 .init = uniphier_u3ssphy_init, 209 .exit = uniphier_u3ssphy_exit, 210 .power_on = uniphier_u3ssphy_power_on, 211 .power_off = uniphier_u3ssphy_power_off, 212 .owner = THIS_MODULE, 213}; 214 215static int uniphier_u3ssphy_probe(struct platform_device *pdev) 216{ 217 struct device *dev = &pdev->dev; 218 struct uniphier_u3ssphy_priv *priv; 219 struct phy_provider *phy_provider; 220 struct phy *phy; 221 222 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 223 if (!priv) 224 return -ENOMEM; 225 226 priv->dev = dev; 227 priv->data = of_device_get_match_data(dev); 228 if (WARN_ON(!priv->data || 229 priv->data->nparams > MAX_PHY_PARAMS)) 230 return -EINVAL; 231 232 priv->base = devm_platform_ioremap_resource(pdev, 0); 233 if (IS_ERR(priv->base)) 234 return PTR_ERR(priv->base); 235 236 if (!priv->data->is_legacy) { 237 priv->clk = devm_clk_get(dev, "phy"); 238 if (IS_ERR(priv->clk)) 239 return PTR_ERR(priv->clk); 240 241 priv->clk_ext = devm_clk_get_optional(dev, "phy-ext"); 242 if (IS_ERR(priv->clk_ext)) 243 return PTR_ERR(priv->clk_ext); 244 245 priv->rst = devm_reset_control_get_shared(dev, "phy"); 246 if (IS_ERR(priv->rst)) 247 return PTR_ERR(priv->rst); 248 } else { 249 priv->clk_parent_gio = devm_clk_get(dev, "gio"); 250 if (IS_ERR(priv->clk_parent_gio)) 251 return PTR_ERR(priv->clk_parent_gio); 252 253 priv->rst_parent_gio = 254 devm_reset_control_get_shared(dev, "gio"); 255 if (IS_ERR(priv->rst_parent_gio)) 256 return PTR_ERR(priv->rst_parent_gio); 257 } 258 259 priv->clk_parent = devm_clk_get(dev, "link"); 260 if (IS_ERR(priv->clk_parent)) 261 return PTR_ERR(priv->clk_parent); 262 263 priv->rst_parent = devm_reset_control_get_shared(dev, "link"); 264 if (IS_ERR(priv->rst_parent)) 265 return PTR_ERR(priv->rst_parent); 266 267 priv->vbus = devm_regulator_get_optional(dev, "vbus"); 268 if (IS_ERR(priv->vbus)) { 269 if (PTR_ERR(priv->vbus) == -EPROBE_DEFER) 270 return PTR_ERR(priv->vbus); 271 priv->vbus = NULL; 272 } 273 274 phy = devm_phy_create(dev, dev->of_node, &uniphier_u3ssphy_ops); 275 if (IS_ERR(phy)) 276 return PTR_ERR(phy); 277 278 phy_set_drvdata(phy, priv); 279 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 280 281 return PTR_ERR_OR_ZERO(phy_provider); 282} 283 284static const struct uniphier_u3ssphy_soc_data uniphier_pro4_data = { 285 .is_legacy = true, 286}; 287 288static const struct uniphier_u3ssphy_soc_data uniphier_pxs2_data = { 289 .is_legacy = false, 290 .nparams = 7, 291 .param = { 292 { CDR_CPD_TRIM, 10 }, 293 { CDR_CPF_TRIM, 3 }, 294 { TX_PLL_TRIM, 5 }, 295 { BGAP_TRIM, 9 }, 296 { CDR_TRIM, 2 }, 297 { VCOPLL_CTRL, 7 }, 298 { VCOPLL_CM, 1 }, 299 }, 300}; 301 302static const struct uniphier_u3ssphy_soc_data uniphier_ld20_data = { 303 .is_legacy = false, 304 .nparams = 3, 305 .param = { 306 { CDR_CPD_TRIM, 6 }, 307 { CDR_TRIM, 2 }, 308 { VCO_CTRL, 5 }, 309 }, 310}; 311 312static const struct of_device_id uniphier_u3ssphy_match[] = { 313 { 314 .compatible = "socionext,uniphier-pro4-usb3-ssphy", 315 .data = &uniphier_pro4_data, 316 }, 317 { 318 .compatible = "socionext,uniphier-pro5-usb3-ssphy", 319 .data = &uniphier_pro4_data, 320 }, 321 { 322 .compatible = "socionext,uniphier-pxs2-usb3-ssphy", 323 .data = &uniphier_pxs2_data, 324 }, 325 { 326 .compatible = "socionext,uniphier-ld20-usb3-ssphy", 327 .data = &uniphier_ld20_data, 328 }, 329 { 330 .compatible = "socionext,uniphier-pxs3-usb3-ssphy", 331 .data = &uniphier_ld20_data, 332 }, 333 { 334 .compatible = "socionext,uniphier-nx1-usb3-ssphy", 335 .data = &uniphier_ld20_data, 336 }, 337 { /* sentinel */ } 338}; 339MODULE_DEVICE_TABLE(of, uniphier_u3ssphy_match); 340 341static struct platform_driver uniphier_u3ssphy_driver = { 342 .probe = uniphier_u3ssphy_probe, 343 .driver = { 344 .name = "uniphier-usb3-ssphy", 345 .of_match_table = uniphier_u3ssphy_match, 346 }, 347}; 348 349module_platform_driver(uniphier_u3ssphy_driver); 350 351MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>"); 352MODULE_DESCRIPTION("UniPhier SS-PHY driver for USB3 controller"); 353MODULE_LICENSE("GPL v2");