Lines Matching +full:exynos5250 +full:- +full:usbdrd +full:- +full:phy
1 // SPDX-License-Identifier: GPL-2.0-only
3 * Samsung Exynos5 SoC series USB DRD PHY driver
5 * Phy provider for USB 3.0 DRD controller on Exynos5 SoC series
20 #include <linux/phy/phy.h>
26 #include <linux/soc/samsung/exynos-regs-pmu.h>
28 /* Exynos USB PHY registers */
37 /* Exynos5: USB 3.0 DRD PHY registers */
130 /* USB 3.0 DRD PHY SS Function Control Reg; accessed by CR_PORT */
176 * struct exynos5_usbdrd_phy - driver data for USB 3.0 PHY
178 * @reg_phy: usb phy controller register memory base
179 * @clk: phy clock for register access
180 * @pipeclk: clock for pipe3 phy
181 * @utmiclk: clock for utmi+ phy
184 * @phys: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY
185 * instances each with its 'phy' and 'phy_cfg'.
188 * @ref_clk: reference clock to PHY block from which PHY's
190 * @vbus: VBUS regulator for phy
202 struct phy *phy; member
218 phys[(inst)->index]); in to_usbdrd_phy()
223 * can be written to the phy register.
252 return -EINVAL; in exynos5_rate_to_clk()
263 if (!inst->reg_pmu) in exynos5_usbdrd_phy_isol()
268 regmap_update_bits(inst->reg_pmu, inst->pmu_offset, in exynos5_usbdrd_phy_isol()
273 * Sets the pipe3 phy's clk as EXTREFCLK (XXTI) which is internal clock
284 reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); in exynos5_usbdrd_pipe3_set_refclk()
294 switch (phy_drd->extrefclk) { in exynos5_usbdrd_pipe3_set_refclk()
312 dev_dbg(phy_drd->dev, "unsupported ref clk\n"); in exynos5_usbdrd_pipe3_set_refclk()
320 * Sets the utmi phy's clk as EXTREFCLK (XXTI) which is internal clock
330 reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); in exynos5_usbdrd_utmi_set_refclk()
338 reg |= PHYCLKRST_FSEL(phy_drd->extrefclk); in exynos5_usbdrd_utmi_set_refclk()
347 reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); in exynos5_usbdrd_pipe3_init()
348 /* Set Tx De-Emphasis level */ in exynos5_usbdrd_pipe3_init()
351 writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); in exynos5_usbdrd_pipe3_init()
353 reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); in exynos5_usbdrd_pipe3_init()
355 writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); in exynos5_usbdrd_pipe3_init()
362 reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); in exynos5_usbdrd_utmi_init()
363 /* Set Loss-of-Signal Detector sensitivity */ in exynos5_usbdrd_utmi_init()
366 writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); in exynos5_usbdrd_utmi_init()
368 reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); in exynos5_usbdrd_utmi_init()
369 /* Set Tx De-Emphasis level */ in exynos5_usbdrd_utmi_init()
372 writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); in exynos5_usbdrd_utmi_init()
375 writel(PHYUTMI_OTGDISABLE, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI); in exynos5_usbdrd_utmi_init()
377 reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); in exynos5_usbdrd_utmi_init()
379 writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); in exynos5_usbdrd_utmi_init()
382 static int exynos5_usbdrd_phy_init(struct phy *phy) in exynos5_usbdrd_phy_init() argument
386 struct phy_usb_instance *inst = phy_get_drvdata(phy); in exynos5_usbdrd_phy_init()
389 ret = clk_prepare_enable(phy_drd->clk); in exynos5_usbdrd_phy_init()
393 /* Reset USB 3.0 PHY */ in exynos5_usbdrd_phy_init()
394 writel(0x0, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); in exynos5_usbdrd_phy_init()
395 writel(0x0, phy_drd->reg_phy + EXYNOS5_DRD_PHYRESUME); in exynos5_usbdrd_phy_init()
403 writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_LINKSYSTEM); in exynos5_usbdrd_phy_init()
405 reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); in exynos5_usbdrd_phy_init()
406 /* Select PHY CLK source */ in exynos5_usbdrd_phy_init()
408 writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); in exynos5_usbdrd_phy_init()
411 reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL); in exynos5_usbdrd_phy_init()
413 writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL); in exynos5_usbdrd_phy_init()
416 inst->phy_cfg->phy_init(phy_drd); in exynos5_usbdrd_phy_init()
419 reg = inst->phy_cfg->set_refclk(inst); in exynos5_usbdrd_phy_init()
432 writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); in exynos5_usbdrd_phy_init()
437 writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); in exynos5_usbdrd_phy_init()
439 clk_disable_unprepare(phy_drd->clk); in exynos5_usbdrd_phy_init()
444 static int exynos5_usbdrd_phy_exit(struct phy *phy) in exynos5_usbdrd_phy_exit() argument
448 struct phy_usb_instance *inst = phy_get_drvdata(phy); in exynos5_usbdrd_phy_exit()
451 ret = clk_prepare_enable(phy_drd->clk); in exynos5_usbdrd_phy_exit()
458 writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI); in exynos5_usbdrd_phy_exit()
461 reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); in exynos5_usbdrd_phy_exit()
465 writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); in exynos5_usbdrd_phy_exit()
468 reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); in exynos5_usbdrd_phy_exit()
471 writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); in exynos5_usbdrd_phy_exit()
473 clk_disable_unprepare(phy_drd->clk); in exynos5_usbdrd_phy_exit()
478 static int exynos5_usbdrd_phy_power_on(struct phy *phy) in exynos5_usbdrd_phy_power_on() argument
481 struct phy_usb_instance *inst = phy_get_drvdata(phy); in exynos5_usbdrd_phy_power_on()
484 dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n"); in exynos5_usbdrd_phy_power_on()
486 clk_prepare_enable(phy_drd->ref_clk); in exynos5_usbdrd_phy_power_on()
487 if (!phy_drd->drv_data->has_common_clk_gate) { in exynos5_usbdrd_phy_power_on()
488 clk_prepare_enable(phy_drd->pipeclk); in exynos5_usbdrd_phy_power_on()
489 clk_prepare_enable(phy_drd->utmiclk); in exynos5_usbdrd_phy_power_on()
490 clk_prepare_enable(phy_drd->itpclk); in exynos5_usbdrd_phy_power_on()
494 if (phy_drd->vbus_boost) { in exynos5_usbdrd_phy_power_on()
495 ret = regulator_enable(phy_drd->vbus_boost); in exynos5_usbdrd_phy_power_on()
497 dev_err(phy_drd->dev, in exynos5_usbdrd_phy_power_on()
503 if (phy_drd->vbus) { in exynos5_usbdrd_phy_power_on()
504 ret = regulator_enable(phy_drd->vbus); in exynos5_usbdrd_phy_power_on()
506 dev_err(phy_drd->dev, "Failed to enable VBUS supply\n"); in exynos5_usbdrd_phy_power_on()
511 /* Power-on PHY*/ in exynos5_usbdrd_phy_power_on()
512 inst->phy_cfg->phy_isol(inst, 0); in exynos5_usbdrd_phy_power_on()
517 if (phy_drd->vbus_boost) in exynos5_usbdrd_phy_power_on()
518 regulator_disable(phy_drd->vbus_boost); in exynos5_usbdrd_phy_power_on()
521 clk_disable_unprepare(phy_drd->ref_clk); in exynos5_usbdrd_phy_power_on()
522 if (!phy_drd->drv_data->has_common_clk_gate) { in exynos5_usbdrd_phy_power_on()
523 clk_disable_unprepare(phy_drd->itpclk); in exynos5_usbdrd_phy_power_on()
524 clk_disable_unprepare(phy_drd->utmiclk); in exynos5_usbdrd_phy_power_on()
525 clk_disable_unprepare(phy_drd->pipeclk); in exynos5_usbdrd_phy_power_on()
531 static int exynos5_usbdrd_phy_power_off(struct phy *phy) in exynos5_usbdrd_phy_power_off() argument
533 struct phy_usb_instance *inst = phy_get_drvdata(phy); in exynos5_usbdrd_phy_power_off()
536 dev_dbg(phy_drd->dev, "Request to power_off usbdrd_phy phy\n"); in exynos5_usbdrd_phy_power_off()
538 /* Power-off the PHY */ in exynos5_usbdrd_phy_power_off()
539 inst->phy_cfg->phy_isol(inst, 1); in exynos5_usbdrd_phy_power_off()
542 if (phy_drd->vbus) in exynos5_usbdrd_phy_power_off()
543 regulator_disable(phy_drd->vbus); in exynos5_usbdrd_phy_power_off()
544 if (phy_drd->vbus_boost) in exynos5_usbdrd_phy_power_off()
545 regulator_disable(phy_drd->vbus_boost); in exynos5_usbdrd_phy_power_off()
547 clk_disable_unprepare(phy_drd->ref_clk); in exynos5_usbdrd_phy_power_off()
548 if (!phy_drd->drv_data->has_common_clk_gate) { in exynos5_usbdrd_phy_power_off()
549 clk_disable_unprepare(phy_drd->itpclk); in exynos5_usbdrd_phy_power_off()
550 clk_disable_unprepare(phy_drd->pipeclk); in exynos5_usbdrd_phy_power_off()
551 clk_disable_unprepare(phy_drd->utmiclk); in exynos5_usbdrd_phy_power_off()
563 writel(val | cmd, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); in crport_handshake()
565 err = readl_poll_timeout(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1, in crport_handshake()
567 if (err == -ETIMEDOUT) { in crport_handshake()
568 dev_err(phy_drd->dev, "CRPORT handshake timeout1 (0x%08x)\n", val); in crport_handshake()
572 writel(val, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); in crport_handshake()
574 err = readl_poll_timeout(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1, in crport_handshake()
576 if (err == -ETIMEDOUT) { in crport_handshake()
577 dev_err(phy_drd->dev, "CRPORT handshake timeout2 (0x%08x)\n", val); in crport_handshake()
591 phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); in crport_ctrl_write()
599 phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); in crport_ctrl_write()
612 * Calibrate few PHY parameters using CR_PORT register to meet
614 * which have 28nm USB 3.0 DRD PHY.
622 * Change los_bias to (0x5) for 28nm PHY from a in exynos5420_usbdrd_phy_calibrate()
634 dev_err(phy_drd->dev, in exynos5420_usbdrd_phy_calibrate()
635 "Failed setting Loss-of-Signal level for SuperSpeed\n"); in exynos5420_usbdrd_phy_calibrate()
640 * Set tx_vboost_lvl to (0x5) for 28nm PHY Tuning, in exynos5420_usbdrd_phy_calibrate()
648 dev_err(phy_drd->dev, in exynos5420_usbdrd_phy_calibrate()
649 "Failed setting Tx-Vboost-Level for SuperSpeed\n"); in exynos5420_usbdrd_phy_calibrate()
655 * desired reference clock of PHY, by tuning the CR_PORT in exynos5420_usbdrd_phy_calibrate()
656 * register LANE0.TX_DEBUG which is internal to PHY. in exynos5420_usbdrd_phy_calibrate()
660 * e.g. Samsung SUM-TSB16S 3.0 USB drive. in exynos5420_usbdrd_phy_calibrate()
662 switch (phy_drd->extrefclk) { in exynos5420_usbdrd_phy_calibrate()
680 dev_err(phy_drd->dev, in exynos5420_usbdrd_phy_calibrate()
686 static struct phy *exynos5_usbdrd_phy_xlate(struct device *dev, in exynos5_usbdrd_phy_xlate()
691 if (WARN_ON(args->args[0] >= EXYNOS5_DRDPHYS_NUM)) in exynos5_usbdrd_phy_xlate()
692 return ERR_PTR(-ENODEV); in exynos5_usbdrd_phy_xlate()
694 return phy_drd->phys[args->args[0]].phy; in exynos5_usbdrd_phy_xlate()
697 static int exynos5_usbdrd_phy_calibrate(struct phy *phy) in exynos5_usbdrd_phy_calibrate() argument
699 struct phy_usb_instance *inst = phy_get_drvdata(phy); in exynos5_usbdrd_phy_calibrate()
702 if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI) in exynos5_usbdrd_phy_calibrate()
721 phy_drd->clk = devm_clk_get(phy_drd->dev, "phy"); in exynos5_usbdrd_phy_clk_handle()
722 if (IS_ERR(phy_drd->clk)) { in exynos5_usbdrd_phy_clk_handle()
723 dev_err(phy_drd->dev, "Failed to get phy clock\n"); in exynos5_usbdrd_phy_clk_handle()
724 return PTR_ERR(phy_drd->clk); in exynos5_usbdrd_phy_clk_handle()
727 phy_drd->ref_clk = devm_clk_get(phy_drd->dev, "ref"); in exynos5_usbdrd_phy_clk_handle()
728 if (IS_ERR(phy_drd->ref_clk)) { in exynos5_usbdrd_phy_clk_handle()
729 dev_err(phy_drd->dev, "Failed to get phy reference clock\n"); in exynos5_usbdrd_phy_clk_handle()
730 return PTR_ERR(phy_drd->ref_clk); in exynos5_usbdrd_phy_clk_handle()
732 ref_rate = clk_get_rate(phy_drd->ref_clk); in exynos5_usbdrd_phy_clk_handle()
734 ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk); in exynos5_usbdrd_phy_clk_handle()
736 dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n", in exynos5_usbdrd_phy_clk_handle()
741 if (!phy_drd->drv_data->has_common_clk_gate) { in exynos5_usbdrd_phy_clk_handle()
742 phy_drd->pipeclk = devm_clk_get(phy_drd->dev, "phy_pipe"); in exynos5_usbdrd_phy_clk_handle()
743 if (IS_ERR(phy_drd->pipeclk)) { in exynos5_usbdrd_phy_clk_handle()
744 dev_info(phy_drd->dev, in exynos5_usbdrd_phy_clk_handle()
745 "PIPE3 phy operational clock not specified\n"); in exynos5_usbdrd_phy_clk_handle()
746 phy_drd->pipeclk = NULL; in exynos5_usbdrd_phy_clk_handle()
749 phy_drd->utmiclk = devm_clk_get(phy_drd->dev, "phy_utmi"); in exynos5_usbdrd_phy_clk_handle()
750 if (IS_ERR(phy_drd->utmiclk)) { in exynos5_usbdrd_phy_clk_handle()
751 dev_info(phy_drd->dev, in exynos5_usbdrd_phy_clk_handle()
752 "UTMI phy operational clock not specified\n"); in exynos5_usbdrd_phy_clk_handle()
753 phy_drd->utmiclk = NULL; in exynos5_usbdrd_phy_clk_handle()
756 phy_drd->itpclk = devm_clk_get(phy_drd->dev, "itp"); in exynos5_usbdrd_phy_clk_handle()
757 if (IS_ERR(phy_drd->itpclk)) { in exynos5_usbdrd_phy_clk_handle()
758 dev_info(phy_drd->dev, in exynos5_usbdrd_phy_clk_handle()
760 phy_drd->itpclk = NULL; in exynos5_usbdrd_phy_clk_handle()
810 .compatible = "samsung,exynos5250-usbdrd-phy",
813 .compatible = "samsung,exynos5420-usbdrd-phy",
816 .compatible = "samsung,exynos5433-usbdrd-phy",
819 .compatible = "samsung,exynos7-usbdrd-phy",
828 struct device *dev = &pdev->dev; in exynos5_usbdrd_phy_probe()
829 struct device_node *node = dev->of_node; in exynos5_usbdrd_phy_probe()
840 return -ENOMEM; in exynos5_usbdrd_phy_probe()
843 phy_drd->dev = dev; in exynos5_usbdrd_phy_probe()
845 phy_drd->reg_phy = devm_platform_ioremap_resource(pdev, 0); in exynos5_usbdrd_phy_probe()
846 if (IS_ERR(phy_drd->reg_phy)) in exynos5_usbdrd_phy_probe()
847 return PTR_ERR(phy_drd->reg_phy); in exynos5_usbdrd_phy_probe()
851 return -EINVAL; in exynos5_usbdrd_phy_probe()
853 phy_drd->drv_data = drv_data; in exynos5_usbdrd_phy_probe()
861 reg_pmu = syscon_regmap_lookup_by_phandle(dev->of_node, in exynos5_usbdrd_phy_probe()
862 "samsung,pmu-syscon"); in exynos5_usbdrd_phy_probe()
869 * Exynos5420 SoC has multiple channels for USB 3.0 PHY, with in exynos5_usbdrd_phy_probe()
875 dev_dbg(dev, "Not a multi-controller usbdrd phy\n"); in exynos5_usbdrd_phy_probe()
879 pmu_offset = phy_drd->drv_data->pmu_offset_usbdrd1_phy; in exynos5_usbdrd_phy_probe()
883 pmu_offset = phy_drd->drv_data->pmu_offset_usbdrd0_phy; in exynos5_usbdrd_phy_probe()
888 phy_drd->vbus = devm_regulator_get(dev, "vbus"); in exynos5_usbdrd_phy_probe()
889 if (IS_ERR(phy_drd->vbus)) { in exynos5_usbdrd_phy_probe()
890 ret = PTR_ERR(phy_drd->vbus); in exynos5_usbdrd_phy_probe()
891 if (ret == -EPROBE_DEFER) in exynos5_usbdrd_phy_probe()
895 phy_drd->vbus = NULL; in exynos5_usbdrd_phy_probe()
898 phy_drd->vbus_boost = devm_regulator_get(dev, "vbus-boost"); in exynos5_usbdrd_phy_probe()
899 if (IS_ERR(phy_drd->vbus_boost)) { in exynos5_usbdrd_phy_probe()
900 ret = PTR_ERR(phy_drd->vbus_boost); in exynos5_usbdrd_phy_probe()
901 if (ret == -EPROBE_DEFER) in exynos5_usbdrd_phy_probe()
905 phy_drd->vbus_boost = NULL; in exynos5_usbdrd_phy_probe()
908 dev_vdbg(dev, "Creating usbdrd_phy phy\n"); in exynos5_usbdrd_phy_probe()
911 struct phy *phy = devm_phy_create(dev, NULL, in exynos5_usbdrd_phy_probe() local
913 if (IS_ERR(phy)) { in exynos5_usbdrd_phy_probe()
914 dev_err(dev, "Failed to create usbdrd_phy phy\n"); in exynos5_usbdrd_phy_probe()
915 return PTR_ERR(phy); in exynos5_usbdrd_phy_probe()
918 phy_drd->phys[i].phy = phy; in exynos5_usbdrd_phy_probe()
919 phy_drd->phys[i].index = i; in exynos5_usbdrd_phy_probe()
920 phy_drd->phys[i].reg_pmu = reg_pmu; in exynos5_usbdrd_phy_probe()
921 phy_drd->phys[i].pmu_offset = pmu_offset; in exynos5_usbdrd_phy_probe()
922 phy_drd->phys[i].phy_cfg = &drv_data->phy_cfg[i]; in exynos5_usbdrd_phy_probe()
923 phy_set_drvdata(phy, &phy_drd->phys[i]); in exynos5_usbdrd_phy_probe()
929 dev_err(phy_drd->dev, "Failed to register phy provider\n"); in exynos5_usbdrd_phy_probe()
946 MODULE_DESCRIPTION("Samsung Exynos5 SoCs USB 3.0 DRD controller PHY driver");