532 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * MediaTek USB3.1 gen2 xsphy Driver4 *5 * Copyright (c) 2018 MediaTek Inc.6 * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>7 *8 */9 10#include <dt-bindings/phy/phy.h>11#include <linux/clk.h>12#include <linux/delay.h>13#include <linux/iopoll.h>14#include <linux/module.h>15#include <linux/of_address.h>16#include <linux/phy/phy.h>17#include <linux/platform_device.h>18 19#include "phy-mtk-io.h"20 21/* u2 phy banks */22#define SSUSB_SIFSLV_MISC 0x00023#define SSUSB_SIFSLV_U2FREQ 0x10024#define SSUSB_SIFSLV_U2PHY_COM 0x30025 26/* u3 phy shared banks */27#define SSPXTP_SIFSLV_DIG_GLB 0x00028#define SSPXTP_SIFSLV_PHYA_GLB 0x10029 30/* u3 phy banks */31#define SSPXTP_SIFSLV_DIG_LN_TOP 0x00032#define SSPXTP_SIFSLV_DIG_LN_TX0 0x10033#define SSPXTP_SIFSLV_DIG_LN_RX0 0x20034#define SSPXTP_SIFSLV_DIG_LN_DAIF 0x30035#define SSPXTP_SIFSLV_PHYA_LN 0x40036 37#define XSP_U2FREQ_FMCR0 ((SSUSB_SIFSLV_U2FREQ) + 0x00)38#define P2F_RG_FREQDET_EN BIT(24)39#define P2F_RG_CYCLECNT GENMASK(23, 0)40 41#define XSP_U2FREQ_MMONR0 ((SSUSB_SIFSLV_U2FREQ) + 0x0c)42 43#define XSP_U2FREQ_FMMONR1 ((SSUSB_SIFSLV_U2FREQ) + 0x10)44#define P2F_RG_FRCK_EN BIT(8)45#define P2F_USB_FM_VALID BIT(0)46 47#define XSP_USBPHYACR0 ((SSUSB_SIFSLV_U2PHY_COM) + 0x00)48#define P2A0_RG_INTR_EN BIT(5)49 50#define XSP_USBPHYACR1 ((SSUSB_SIFSLV_U2PHY_COM) + 0x04)51#define P2A1_RG_INTR_CAL GENMASK(23, 19)52#define P2A1_RG_VRT_SEL GENMASK(14, 12)53#define P2A1_RG_TERM_SEL GENMASK(10, 8)54 55#define XSP_USBPHYACR5 ((SSUSB_SIFSLV_U2PHY_COM) + 0x014)56#define P2A5_RG_HSTX_SRCAL_EN BIT(15)57#define P2A5_RG_HSTX_SRCTRL GENMASK(14, 12)58 59#define XSP_USBPHYACR6 ((SSUSB_SIFSLV_U2PHY_COM) + 0x018)60#define P2A6_RG_BC11_SW_EN BIT(23)61#define P2A6_RG_OTG_VBUSCMP_EN BIT(20)62 63#define XSP_U2PHYDTM1 ((SSUSB_SIFSLV_U2PHY_COM) + 0x06C)64#define P2D_FORCE_IDDIG BIT(9)65#define P2D_RG_VBUSVALID BIT(5)66#define P2D_RG_SESSEND BIT(4)67#define P2D_RG_AVALID BIT(2)68#define P2D_RG_IDDIG BIT(1)69 70#define SSPXTP_PHYA_GLB_00 ((SSPXTP_SIFSLV_PHYA_GLB) + 0x00)71#define RG_XTP_GLB_BIAS_INTR_CTRL GENMASK(21, 16)72 73#define SSPXTP_PHYA_LN_04 ((SSPXTP_SIFSLV_PHYA_LN) + 0x04)74#define RG_XTP_LN0_TX_IMPSEL GENMASK(4, 0)75 76#define SSPXTP_PHYA_LN_14 ((SSPXTP_SIFSLV_PHYA_LN) + 0x014)77#define RG_XTP_LN0_RX_IMPSEL GENMASK(4, 0)78 79#define XSP_REF_CLK 26 /* MHZ */80#define XSP_SLEW_RATE_COEF 1781#define XSP_SR_COEF_DIVISOR 100082#define XSP_FM_DET_CYCLE_CNT 102483 84struct xsphy_instance {85 struct phy *phy;86 void __iomem *port_base;87 struct clk *ref_clk; /* reference clock of anolog phy */88 u32 index;89 u32 type;90 /* only for HQA test */91 int efuse_intr;92 int efuse_tx_imp;93 int efuse_rx_imp;94 /* u2 eye diagram */95 int eye_src;96 int eye_vrt;97 int eye_term;98};99 100struct mtk_xsphy {101 struct device *dev;102 void __iomem *glb_base; /* only shared u3 sif */103 struct xsphy_instance **phys;104 int nphys;105 int src_ref_clk; /* MHZ, reference clock for slew rate calibrate */106 int src_coef; /* coefficient for slew rate calibrate */107};108 109static void u2_phy_slew_rate_calibrate(struct mtk_xsphy *xsphy,110 struct xsphy_instance *inst)111{112 void __iomem *pbase = inst->port_base;113 int calib_val;114 int fm_out;115 u32 tmp;116 117 /* use force value */118 if (inst->eye_src)119 return;120 121 /* enable USB ring oscillator */122 mtk_phy_set_bits(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCAL_EN);123 udelay(1); /* wait clock stable */124 125 /* enable free run clock */126 mtk_phy_set_bits(pbase + XSP_U2FREQ_FMMONR1, P2F_RG_FRCK_EN);127 128 /* set cycle count as 1024 */129 mtk_phy_update_field(pbase + XSP_U2FREQ_FMCR0, P2F_RG_CYCLECNT,130 XSP_FM_DET_CYCLE_CNT);131 132 /* enable frequency meter */133 mtk_phy_set_bits(pbase + XSP_U2FREQ_FMCR0, P2F_RG_FREQDET_EN);134 135 /* ignore return value */136 readl_poll_timeout(pbase + XSP_U2FREQ_FMMONR1, tmp,137 (tmp & P2F_USB_FM_VALID), 10, 200);138 139 fm_out = readl(pbase + XSP_U2FREQ_MMONR0);140 141 /* disable frequency meter */142 mtk_phy_clear_bits(pbase + XSP_U2FREQ_FMCR0, P2F_RG_FREQDET_EN);143 144 /* disable free run clock */145 mtk_phy_clear_bits(pbase + XSP_U2FREQ_FMMONR1, P2F_RG_FRCK_EN);146 147 if (fm_out) {148 /* (1024 / FM_OUT) x reference clock frequency x coefficient */149 tmp = xsphy->src_ref_clk * xsphy->src_coef;150 tmp = (tmp * XSP_FM_DET_CYCLE_CNT) / fm_out;151 calib_val = DIV_ROUND_CLOSEST(tmp, XSP_SR_COEF_DIVISOR);152 } else {153 /* if FM detection fail, set default value */154 calib_val = 3;155 }156 dev_dbg(xsphy->dev, "phy.%d, fm_out:%d, calib:%d (clk:%d, coef:%d)\n",157 inst->index, fm_out, calib_val,158 xsphy->src_ref_clk, xsphy->src_coef);159 160 /* set HS slew rate */161 mtk_phy_update_field(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCTRL, calib_val);162 163 /* disable USB ring oscillator */164 mtk_phy_clear_bits(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCAL_EN);165}166 167static void u2_phy_instance_init(struct mtk_xsphy *xsphy,168 struct xsphy_instance *inst)169{170 void __iomem *pbase = inst->port_base;171 172 /* DP/DM BC1.1 path Disable */173 mtk_phy_clear_bits(pbase + XSP_USBPHYACR6, P2A6_RG_BC11_SW_EN);174 175 mtk_phy_set_bits(pbase + XSP_USBPHYACR0, P2A0_RG_INTR_EN);176}177 178static void u2_phy_instance_power_on(struct mtk_xsphy *xsphy,179 struct xsphy_instance *inst)180{181 void __iomem *pbase = inst->port_base;182 u32 index = inst->index;183 184 mtk_phy_set_bits(pbase + XSP_USBPHYACR6, P2A6_RG_OTG_VBUSCMP_EN);185 186 mtk_phy_update_bits(pbase + XSP_U2PHYDTM1,187 P2D_RG_VBUSVALID | P2D_RG_AVALID | P2D_RG_SESSEND,188 P2D_RG_VBUSVALID | P2D_RG_AVALID);189 190 dev_dbg(xsphy->dev, "%s(%d)\n", __func__, index);191}192 193static void u2_phy_instance_power_off(struct mtk_xsphy *xsphy,194 struct xsphy_instance *inst)195{196 void __iomem *pbase = inst->port_base;197 u32 index = inst->index;198 199 mtk_phy_clear_bits(pbase + XSP_USBPHYACR6, P2A6_RG_OTG_VBUSCMP_EN);200 201 mtk_phy_update_bits(pbase + XSP_U2PHYDTM1,202 P2D_RG_VBUSVALID | P2D_RG_AVALID | P2D_RG_SESSEND,203 P2D_RG_SESSEND);204 205 dev_dbg(xsphy->dev, "%s(%d)\n", __func__, index);206}207 208static void u2_phy_instance_set_mode(struct mtk_xsphy *xsphy,209 struct xsphy_instance *inst,210 enum phy_mode mode)211{212 u32 tmp;213 214 tmp = readl(inst->port_base + XSP_U2PHYDTM1);215 switch (mode) {216 case PHY_MODE_USB_DEVICE:217 tmp |= P2D_FORCE_IDDIG | P2D_RG_IDDIG;218 break;219 case PHY_MODE_USB_HOST:220 tmp |= P2D_FORCE_IDDIG;221 tmp &= ~P2D_RG_IDDIG;222 break;223 case PHY_MODE_USB_OTG:224 tmp &= ~(P2D_FORCE_IDDIG | P2D_RG_IDDIG);225 break;226 default:227 return;228 }229 writel(tmp, inst->port_base + XSP_U2PHYDTM1);230}231 232static void phy_parse_property(struct mtk_xsphy *xsphy,233 struct xsphy_instance *inst)234{235 struct device *dev = &inst->phy->dev;236 237 switch (inst->type) {238 case PHY_TYPE_USB2:239 device_property_read_u32(dev, "mediatek,efuse-intr",240 &inst->efuse_intr);241 device_property_read_u32(dev, "mediatek,eye-src",242 &inst->eye_src);243 device_property_read_u32(dev, "mediatek,eye-vrt",244 &inst->eye_vrt);245 device_property_read_u32(dev, "mediatek,eye-term",246 &inst->eye_term);247 dev_dbg(dev, "intr:%d, src:%d, vrt:%d, term:%d\n",248 inst->efuse_intr, inst->eye_src,249 inst->eye_vrt, inst->eye_term);250 break;251 case PHY_TYPE_USB3:252 device_property_read_u32(dev, "mediatek,efuse-intr",253 &inst->efuse_intr);254 device_property_read_u32(dev, "mediatek,efuse-tx-imp",255 &inst->efuse_tx_imp);256 device_property_read_u32(dev, "mediatek,efuse-rx-imp",257 &inst->efuse_rx_imp);258 dev_dbg(dev, "intr:%d, tx-imp:%d, rx-imp:%d\n",259 inst->efuse_intr, inst->efuse_tx_imp,260 inst->efuse_rx_imp);261 break;262 default:263 dev_err(xsphy->dev, "incompatible phy type\n");264 return;265 }266}267 268static void u2_phy_props_set(struct mtk_xsphy *xsphy,269 struct xsphy_instance *inst)270{271 void __iomem *pbase = inst->port_base;272 273 if (inst->efuse_intr)274 mtk_phy_update_field(pbase + XSP_USBPHYACR1, P2A1_RG_INTR_CAL,275 inst->efuse_intr);276 277 if (inst->eye_src)278 mtk_phy_update_field(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCTRL,279 inst->eye_src);280 281 if (inst->eye_vrt)282 mtk_phy_update_field(pbase + XSP_USBPHYACR1, P2A1_RG_VRT_SEL,283 inst->eye_vrt);284 285 if (inst->eye_term)286 mtk_phy_update_field(pbase + XSP_USBPHYACR1, P2A1_RG_TERM_SEL,287 inst->eye_term);288}289 290static void u3_phy_props_set(struct mtk_xsphy *xsphy,291 struct xsphy_instance *inst)292{293 void __iomem *pbase = inst->port_base;294 295 if (inst->efuse_intr)296 mtk_phy_update_field(xsphy->glb_base + SSPXTP_PHYA_GLB_00,297 RG_XTP_GLB_BIAS_INTR_CTRL, inst->efuse_intr);298 299 if (inst->efuse_tx_imp)300 mtk_phy_update_field(pbase + SSPXTP_PHYA_LN_04,301 RG_XTP_LN0_TX_IMPSEL, inst->efuse_tx_imp);302 303 if (inst->efuse_rx_imp)304 mtk_phy_update_field(pbase + SSPXTP_PHYA_LN_14,305 RG_XTP_LN0_RX_IMPSEL, inst->efuse_rx_imp);306}307 308static int mtk_phy_init(struct phy *phy)309{310 struct xsphy_instance *inst = phy_get_drvdata(phy);311 struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent);312 int ret;313 314 ret = clk_prepare_enable(inst->ref_clk);315 if (ret) {316 dev_err(xsphy->dev, "failed to enable ref_clk\n");317 return ret;318 }319 320 switch (inst->type) {321 case PHY_TYPE_USB2:322 u2_phy_instance_init(xsphy, inst);323 u2_phy_props_set(xsphy, inst);324 break;325 case PHY_TYPE_USB3:326 u3_phy_props_set(xsphy, inst);327 break;328 default:329 dev_err(xsphy->dev, "incompatible phy type\n");330 clk_disable_unprepare(inst->ref_clk);331 return -EINVAL;332 }333 334 return 0;335}336 337static int mtk_phy_power_on(struct phy *phy)338{339 struct xsphy_instance *inst = phy_get_drvdata(phy);340 struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent);341 342 if (inst->type == PHY_TYPE_USB2) {343 u2_phy_instance_power_on(xsphy, inst);344 u2_phy_slew_rate_calibrate(xsphy, inst);345 }346 347 return 0;348}349 350static int mtk_phy_power_off(struct phy *phy)351{352 struct xsphy_instance *inst = phy_get_drvdata(phy);353 struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent);354 355 if (inst->type == PHY_TYPE_USB2)356 u2_phy_instance_power_off(xsphy, inst);357 358 return 0;359}360 361static int mtk_phy_exit(struct phy *phy)362{363 struct xsphy_instance *inst = phy_get_drvdata(phy);364 365 clk_disable_unprepare(inst->ref_clk);366 return 0;367}368 369static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode)370{371 struct xsphy_instance *inst = phy_get_drvdata(phy);372 struct mtk_xsphy *xsphy = dev_get_drvdata(phy->dev.parent);373 374 if (inst->type == PHY_TYPE_USB2)375 u2_phy_instance_set_mode(xsphy, inst, mode);376 377 return 0;378}379 380static struct phy *mtk_phy_xlate(struct device *dev,381 const struct of_phandle_args *args)382{383 struct mtk_xsphy *xsphy = dev_get_drvdata(dev);384 struct xsphy_instance *inst = NULL;385 struct device_node *phy_np = args->np;386 int index;387 388 if (args->args_count != 1) {389 dev_err(dev, "invalid number of cells in 'phy' property\n");390 return ERR_PTR(-EINVAL);391 }392 393 for (index = 0; index < xsphy->nphys; index++)394 if (phy_np == xsphy->phys[index]->phy->dev.of_node) {395 inst = xsphy->phys[index];396 break;397 }398 399 if (!inst) {400 dev_err(dev, "failed to find appropriate phy\n");401 return ERR_PTR(-EINVAL);402 }403 404 inst->type = args->args[0];405 if (!(inst->type == PHY_TYPE_USB2 ||406 inst->type == PHY_TYPE_USB3)) {407 dev_err(dev, "unsupported phy type: %d\n", inst->type);408 return ERR_PTR(-EINVAL);409 }410 411 phy_parse_property(xsphy, inst);412 413 return inst->phy;414}415 416static const struct phy_ops mtk_xsphy_ops = {417 .init = mtk_phy_init,418 .exit = mtk_phy_exit,419 .power_on = mtk_phy_power_on,420 .power_off = mtk_phy_power_off,421 .set_mode = mtk_phy_set_mode,422 .owner = THIS_MODULE,423};424 425static const struct of_device_id mtk_xsphy_id_table[] = {426 { .compatible = "mediatek,xsphy", },427 { },428};429MODULE_DEVICE_TABLE(of, mtk_xsphy_id_table);430 431static int mtk_xsphy_probe(struct platform_device *pdev)432{433 struct device *dev = &pdev->dev;434 struct device_node *np = dev->of_node;435 struct phy_provider *provider;436 struct resource *glb_res;437 struct mtk_xsphy *xsphy;438 struct resource res;439 int port;440 441 xsphy = devm_kzalloc(dev, sizeof(*xsphy), GFP_KERNEL);442 if (!xsphy)443 return -ENOMEM;444 445 xsphy->nphys = of_get_child_count(np);446 xsphy->phys = devm_kcalloc(dev, xsphy->nphys,447 sizeof(*xsphy->phys), GFP_KERNEL);448 if (!xsphy->phys)449 return -ENOMEM;450 451 xsphy->dev = dev;452 platform_set_drvdata(pdev, xsphy);453 454 glb_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);455 /* optional, may not exist if no u3 phys */456 if (glb_res) {457 /* get banks shared by multiple u3 phys */458 xsphy->glb_base = devm_ioremap_resource(dev, glb_res);459 if (IS_ERR(xsphy->glb_base)) {460 dev_err(dev, "failed to remap glb regs\n");461 return PTR_ERR(xsphy->glb_base);462 }463 }464 465 xsphy->src_ref_clk = XSP_REF_CLK;466 xsphy->src_coef = XSP_SLEW_RATE_COEF;467 /* update parameters of slew rate calibrate if exist */468 device_property_read_u32(dev, "mediatek,src-ref-clk-mhz",469 &xsphy->src_ref_clk);470 device_property_read_u32(dev, "mediatek,src-coef", &xsphy->src_coef);471 472 port = 0;473 for_each_child_of_node_scoped(np, child_np) {474 struct xsphy_instance *inst;475 struct phy *phy;476 int retval;477 478 inst = devm_kzalloc(dev, sizeof(*inst), GFP_KERNEL);479 if (!inst)480 return -ENOMEM;481 482 xsphy->phys[port] = inst;483 484 phy = devm_phy_create(dev, child_np, &mtk_xsphy_ops);485 if (IS_ERR(phy)) {486 dev_err(dev, "failed to create phy\n");487 return PTR_ERR(phy);488 }489 490 retval = of_address_to_resource(child_np, 0, &res);491 if (retval) {492 dev_err(dev, "failed to get address resource(id-%d)\n",493 port);494 return retval;495 }496 497 inst->port_base = devm_ioremap_resource(&phy->dev, &res);498 if (IS_ERR(inst->port_base)) {499 dev_err(dev, "failed to remap phy regs\n");500 return PTR_ERR(inst->port_base);501 }502 503 inst->phy = phy;504 inst->index = port;505 phy_set_drvdata(phy, inst);506 port++;507 508 inst->ref_clk = devm_clk_get(&phy->dev, "ref");509 if (IS_ERR(inst->ref_clk)) {510 dev_err(dev, "failed to get ref_clk(id-%d)\n", port);511 return PTR_ERR(inst->ref_clk);512 }513 }514 515 provider = devm_of_phy_provider_register(dev, mtk_phy_xlate);516 return PTR_ERR_OR_ZERO(provider);517}518 519static struct platform_driver mtk_xsphy_driver = {520 .probe = mtk_xsphy_probe,521 .driver = {522 .name = "mtk-xsphy",523 .of_match_table = mtk_xsphy_id_table,524 },525};526 527module_platform_driver(mtk_xsphy_driver);528 529MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun@mediatek.com>");530MODULE_DESCRIPTION("MediaTek USB XS-PHY driver");531MODULE_LICENSE("GPL v2");532