Skip to content

Commit 6915250

Browse files
Sam Protsenkovinodkoul
authored andcommitted
phy: exynos5-usbdrd: Add Exynos850 support
Implement Exynos850 USB 2.0 DRD PHY controller support. Exynos850 has quite a different PHY controller than Exynos5 compatible controllers, but it's still possible to implement it on top of existing exynos5-usbdrd driver infrastructure. Only UTMI+ (USB 2.0) PHY interface is implemented, as Exynos850 doesn't support USB 3.0. Only two clocks are used for this controller: - phy: bus clock, used for PHY registers access - ref: PHY reference clock (OSCCLK) Signed-off-by: Sam Protsenko <semen.protsenko@linaro.org> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Link: https://lore.kernel.org/r/20230819031731.22618-7-semen.protsenko@linaro.org Signed-off-by: Vinod Koul <vkoul@kernel.org>
1 parent 255ec38 commit 6915250

File tree

1 file changed

+169
-0
lines changed

1 file changed

+169
-0
lines changed

drivers/phy/samsung/phy-exynos5-usbdrd.c

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,34 @@
145145
#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_62M5 (0x20 << 4)
146146
#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_96M_100M (0x40 << 4)
147147

148+
/* Exynos850: USB DRD PHY registers */
149+
#define EXYNOS850_DRD_LINKCTRL 0x04
150+
#define LINKCTRL_BUS_FILTER_BYPASS(_x) ((_x) << 4)
151+
#define LINKCTRL_FORCE_QACT BIT(8)
152+
153+
#define EXYNOS850_DRD_CLKRST 0x20
154+
#define CLKRST_LINK_SW_RST BIT(0)
155+
#define CLKRST_PORT_RST BIT(1)
156+
#define CLKRST_PHY_SW_RST BIT(3)
157+
158+
#define EXYNOS850_DRD_UTMI 0x50
159+
#define UTMI_FORCE_SLEEP BIT(0)
160+
#define UTMI_FORCE_SUSPEND BIT(1)
161+
#define UTMI_DM_PULLDOWN BIT(2)
162+
#define UTMI_DP_PULLDOWN BIT(3)
163+
#define UTMI_FORCE_BVALID BIT(4)
164+
#define UTMI_FORCE_VBUSVALID BIT(5)
165+
166+
#define EXYNOS850_DRD_HSP 0x54
167+
#define HSP_COMMONONN BIT(8)
168+
#define HSP_EN_UTMISUSPEND BIT(9)
169+
#define HSP_VBUSVLDEXT BIT(12)
170+
#define HSP_VBUSVLDEXTSEL BIT(13)
171+
#define HSP_FSV_OUT_EN BIT(24)
172+
173+
#define EXYNOS850_DRD_HSP_TEST 0x5c
174+
#define HSP_TEST_SIDDQ BIT(24)
175+
148176
#define KHZ 1000
149177
#define MHZ (KHZ * KHZ)
150178

@@ -716,6 +744,129 @@ static const struct phy_ops exynos5_usbdrd_phy_ops = {
716744
.owner = THIS_MODULE,
717745
};
718746

747+
static void exynos850_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
748+
{
749+
void __iomem *regs_base = phy_drd->reg_phy;
750+
u32 reg;
751+
752+
/*
753+
* Disable HWACG (hardware auto clock gating control). This will force
754+
* QACTIVE signal in Q-Channel interface to HIGH level, to make sure
755+
* the PHY clock is not gated by the hardware.
756+
*/
757+
reg = readl(regs_base + EXYNOS850_DRD_LINKCTRL);
758+
reg |= LINKCTRL_FORCE_QACT;
759+
writel(reg, regs_base + EXYNOS850_DRD_LINKCTRL);
760+
761+
/* Start PHY Reset (POR=high) */
762+
reg = readl(regs_base + EXYNOS850_DRD_CLKRST);
763+
reg |= CLKRST_PHY_SW_RST;
764+
writel(reg, regs_base + EXYNOS850_DRD_CLKRST);
765+
766+
/* Enable UTMI+ */
767+
reg = readl(regs_base + EXYNOS850_DRD_UTMI);
768+
reg &= ~(UTMI_FORCE_SUSPEND | UTMI_FORCE_SLEEP | UTMI_DP_PULLDOWN |
769+
UTMI_DM_PULLDOWN);
770+
writel(reg, regs_base + EXYNOS850_DRD_UTMI);
771+
772+
/* Set PHY clock and control HS PHY */
773+
reg = readl(regs_base + EXYNOS850_DRD_HSP);
774+
reg |= HSP_EN_UTMISUSPEND | HSP_COMMONONN;
775+
writel(reg, regs_base + EXYNOS850_DRD_HSP);
776+
777+
/* Set VBUS Valid and D+ pull-up control by VBUS pad usage */
778+
reg = readl(regs_base + EXYNOS850_DRD_LINKCTRL);
779+
reg |= LINKCTRL_BUS_FILTER_BYPASS(0xf);
780+
writel(reg, regs_base + EXYNOS850_DRD_LINKCTRL);
781+
782+
reg = readl(regs_base + EXYNOS850_DRD_UTMI);
783+
reg |= UTMI_FORCE_BVALID | UTMI_FORCE_VBUSVALID;
784+
writel(reg, regs_base + EXYNOS850_DRD_UTMI);
785+
786+
reg = readl(regs_base + EXYNOS850_DRD_HSP);
787+
reg |= HSP_VBUSVLDEXT | HSP_VBUSVLDEXTSEL;
788+
writel(reg, regs_base + EXYNOS850_DRD_HSP);
789+
790+
/* Power up PHY analog blocks */
791+
reg = readl(regs_base + EXYNOS850_DRD_HSP_TEST);
792+
reg &= ~HSP_TEST_SIDDQ;
793+
writel(reg, regs_base + EXYNOS850_DRD_HSP_TEST);
794+
795+
/* Finish PHY reset (POR=low) */
796+
udelay(10); /* required before doing POR=low */
797+
reg = readl(regs_base + EXYNOS850_DRD_CLKRST);
798+
reg &= ~(CLKRST_PHY_SW_RST | CLKRST_PORT_RST);
799+
writel(reg, regs_base + EXYNOS850_DRD_CLKRST);
800+
udelay(75); /* required after POR=low for guaranteed PHY clock */
801+
802+
/* Disable single ended signal out */
803+
reg = readl(regs_base + EXYNOS850_DRD_HSP);
804+
reg &= ~HSP_FSV_OUT_EN;
805+
writel(reg, regs_base + EXYNOS850_DRD_HSP);
806+
}
807+
808+
static int exynos850_usbdrd_phy_init(struct phy *phy)
809+
{
810+
struct phy_usb_instance *inst = phy_get_drvdata(phy);
811+
struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
812+
int ret;
813+
814+
ret = clk_prepare_enable(phy_drd->clk);
815+
if (ret)
816+
return ret;
817+
818+
/* UTMI or PIPE3 specific init */
819+
inst->phy_cfg->phy_init(phy_drd);
820+
821+
clk_disable_unprepare(phy_drd->clk);
822+
823+
return 0;
824+
}
825+
826+
static int exynos850_usbdrd_phy_exit(struct phy *phy)
827+
{
828+
struct phy_usb_instance *inst = phy_get_drvdata(phy);
829+
struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
830+
void __iomem *regs_base = phy_drd->reg_phy;
831+
u32 reg;
832+
int ret;
833+
834+
ret = clk_prepare_enable(phy_drd->clk);
835+
if (ret)
836+
return ret;
837+
838+
/* Set PHY clock and control HS PHY */
839+
reg = readl(regs_base + EXYNOS850_DRD_UTMI);
840+
reg &= ~(UTMI_DP_PULLDOWN | UTMI_DM_PULLDOWN);
841+
reg |= UTMI_FORCE_SUSPEND | UTMI_FORCE_SLEEP;
842+
writel(reg, regs_base + EXYNOS850_DRD_UTMI);
843+
844+
/* Power down PHY analog blocks */
845+
reg = readl(regs_base + EXYNOS850_DRD_HSP_TEST);
846+
reg |= HSP_TEST_SIDDQ;
847+
writel(reg, regs_base + EXYNOS850_DRD_HSP_TEST);
848+
849+
/* Link reset */
850+
reg = readl(regs_base + EXYNOS850_DRD_CLKRST);
851+
reg |= CLKRST_LINK_SW_RST;
852+
writel(reg, regs_base + EXYNOS850_DRD_CLKRST);
853+
udelay(10); /* required before doing POR=low */
854+
reg &= ~CLKRST_LINK_SW_RST;
855+
writel(reg, regs_base + EXYNOS850_DRD_CLKRST);
856+
857+
clk_disable_unprepare(phy_drd->clk);
858+
859+
return 0;
860+
}
861+
862+
static const struct phy_ops exynos850_usbdrd_phy_ops = {
863+
.init = exynos850_usbdrd_phy_init,
864+
.exit = exynos850_usbdrd_phy_exit,
865+
.power_on = exynos5_usbdrd_phy_power_on,
866+
.power_off = exynos5_usbdrd_phy_power_off,
867+
.owner = THIS_MODULE,
868+
};
869+
719870
static int exynos5_usbdrd_phy_clk_handle(struct exynos5_usbdrd_phy *phy_drd)
720871
{
721872
unsigned long ref_rate;
@@ -782,6 +933,14 @@ static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = {
782933
},
783934
};
784935

936+
static const struct exynos5_usbdrd_phy_config phy_cfg_exynos850[] = {
937+
{
938+
.id = EXYNOS5_DRDPHY_UTMI,
939+
.phy_isol = exynos5_usbdrd_phy_isol,
940+
.phy_init = exynos850_usbdrd_utmi_init,
941+
},
942+
};
943+
785944
static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = {
786945
.phy_cfg = phy_cfg_exynos5,
787946
.phy_ops = &exynos5_usbdrd_phy_ops,
@@ -812,6 +971,13 @@ static const struct exynos5_usbdrd_phy_drvdata exynos7_usbdrd_phy = {
812971
.has_common_clk_gate = false,
813972
};
814973

974+
static const struct exynos5_usbdrd_phy_drvdata exynos850_usbdrd_phy = {
975+
.phy_cfg = phy_cfg_exynos850,
976+
.phy_ops = &exynos850_usbdrd_phy_ops,
977+
.pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
978+
.has_common_clk_gate = true,
979+
};
980+
815981
static const struct of_device_id exynos5_usbdrd_phy_of_match[] = {
816982
{
817983
.compatible = "samsung,exynos5250-usbdrd-phy",
@@ -825,6 +991,9 @@ static const struct of_device_id exynos5_usbdrd_phy_of_match[] = {
825991
}, {
826992
.compatible = "samsung,exynos7-usbdrd-phy",
827993
.data = &exynos7_usbdrd_phy
994+
}, {
995+
.compatible = "samsung,exynos850-usbdrd-phy",
996+
.data = &exynos850_usbdrd_phy
828997
},
829998
{ },
830999
};

0 commit comments

Comments
 (0)