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>
This commit is contained in:
parent
255ec3879d
commit
691525074d
|
@ -145,6 +145,34 @@
|
||||||
#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_62M5 (0x20 << 4)
|
#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_62M5 (0x20 << 4)
|
||||||
#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_96M_100M (0x40 << 4)
|
#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_96M_100M (0x40 << 4)
|
||||||
|
|
||||||
|
/* Exynos850: USB DRD PHY registers */
|
||||||
|
#define EXYNOS850_DRD_LINKCTRL 0x04
|
||||||
|
#define LINKCTRL_BUS_FILTER_BYPASS(_x) ((_x) << 4)
|
||||||
|
#define LINKCTRL_FORCE_QACT BIT(8)
|
||||||
|
|
||||||
|
#define EXYNOS850_DRD_CLKRST 0x20
|
||||||
|
#define CLKRST_LINK_SW_RST BIT(0)
|
||||||
|
#define CLKRST_PORT_RST BIT(1)
|
||||||
|
#define CLKRST_PHY_SW_RST BIT(3)
|
||||||
|
|
||||||
|
#define EXYNOS850_DRD_UTMI 0x50
|
||||||
|
#define UTMI_FORCE_SLEEP BIT(0)
|
||||||
|
#define UTMI_FORCE_SUSPEND BIT(1)
|
||||||
|
#define UTMI_DM_PULLDOWN BIT(2)
|
||||||
|
#define UTMI_DP_PULLDOWN BIT(3)
|
||||||
|
#define UTMI_FORCE_BVALID BIT(4)
|
||||||
|
#define UTMI_FORCE_VBUSVALID BIT(5)
|
||||||
|
|
||||||
|
#define EXYNOS850_DRD_HSP 0x54
|
||||||
|
#define HSP_COMMONONN BIT(8)
|
||||||
|
#define HSP_EN_UTMISUSPEND BIT(9)
|
||||||
|
#define HSP_VBUSVLDEXT BIT(12)
|
||||||
|
#define HSP_VBUSVLDEXTSEL BIT(13)
|
||||||
|
#define HSP_FSV_OUT_EN BIT(24)
|
||||||
|
|
||||||
|
#define EXYNOS850_DRD_HSP_TEST 0x5c
|
||||||
|
#define HSP_TEST_SIDDQ BIT(24)
|
||||||
|
|
||||||
#define KHZ 1000
|
#define KHZ 1000
|
||||||
#define MHZ (KHZ * KHZ)
|
#define MHZ (KHZ * KHZ)
|
||||||
|
|
||||||
|
@ -716,6 +744,129 @@ static const struct phy_ops exynos5_usbdrd_phy_ops = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void exynos850_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
|
||||||
|
{
|
||||||
|
void __iomem *regs_base = phy_drd->reg_phy;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable HWACG (hardware auto clock gating control). This will force
|
||||||
|
* QACTIVE signal in Q-Channel interface to HIGH level, to make sure
|
||||||
|
* the PHY clock is not gated by the hardware.
|
||||||
|
*/
|
||||||
|
reg = readl(regs_base + EXYNOS850_DRD_LINKCTRL);
|
||||||
|
reg |= LINKCTRL_FORCE_QACT;
|
||||||
|
writel(reg, regs_base + EXYNOS850_DRD_LINKCTRL);
|
||||||
|
|
||||||
|
/* Start PHY Reset (POR=high) */
|
||||||
|
reg = readl(regs_base + EXYNOS850_DRD_CLKRST);
|
||||||
|
reg |= CLKRST_PHY_SW_RST;
|
||||||
|
writel(reg, regs_base + EXYNOS850_DRD_CLKRST);
|
||||||
|
|
||||||
|
/* Enable UTMI+ */
|
||||||
|
reg = readl(regs_base + EXYNOS850_DRD_UTMI);
|
||||||
|
reg &= ~(UTMI_FORCE_SUSPEND | UTMI_FORCE_SLEEP | UTMI_DP_PULLDOWN |
|
||||||
|
UTMI_DM_PULLDOWN);
|
||||||
|
writel(reg, regs_base + EXYNOS850_DRD_UTMI);
|
||||||
|
|
||||||
|
/* Set PHY clock and control HS PHY */
|
||||||
|
reg = readl(regs_base + EXYNOS850_DRD_HSP);
|
||||||
|
reg |= HSP_EN_UTMISUSPEND | HSP_COMMONONN;
|
||||||
|
writel(reg, regs_base + EXYNOS850_DRD_HSP);
|
||||||
|
|
||||||
|
/* Set VBUS Valid and D+ pull-up control by VBUS pad usage */
|
||||||
|
reg = readl(regs_base + EXYNOS850_DRD_LINKCTRL);
|
||||||
|
reg |= LINKCTRL_BUS_FILTER_BYPASS(0xf);
|
||||||
|
writel(reg, regs_base + EXYNOS850_DRD_LINKCTRL);
|
||||||
|
|
||||||
|
reg = readl(regs_base + EXYNOS850_DRD_UTMI);
|
||||||
|
reg |= UTMI_FORCE_BVALID | UTMI_FORCE_VBUSVALID;
|
||||||
|
writel(reg, regs_base + EXYNOS850_DRD_UTMI);
|
||||||
|
|
||||||
|
reg = readl(regs_base + EXYNOS850_DRD_HSP);
|
||||||
|
reg |= HSP_VBUSVLDEXT | HSP_VBUSVLDEXTSEL;
|
||||||
|
writel(reg, regs_base + EXYNOS850_DRD_HSP);
|
||||||
|
|
||||||
|
/* Power up PHY analog blocks */
|
||||||
|
reg = readl(regs_base + EXYNOS850_DRD_HSP_TEST);
|
||||||
|
reg &= ~HSP_TEST_SIDDQ;
|
||||||
|
writel(reg, regs_base + EXYNOS850_DRD_HSP_TEST);
|
||||||
|
|
||||||
|
/* Finish PHY reset (POR=low) */
|
||||||
|
udelay(10); /* required before doing POR=low */
|
||||||
|
reg = readl(regs_base + EXYNOS850_DRD_CLKRST);
|
||||||
|
reg &= ~(CLKRST_PHY_SW_RST | CLKRST_PORT_RST);
|
||||||
|
writel(reg, regs_base + EXYNOS850_DRD_CLKRST);
|
||||||
|
udelay(75); /* required after POR=low for guaranteed PHY clock */
|
||||||
|
|
||||||
|
/* Disable single ended signal out */
|
||||||
|
reg = readl(regs_base + EXYNOS850_DRD_HSP);
|
||||||
|
reg &= ~HSP_FSV_OUT_EN;
|
||||||
|
writel(reg, regs_base + EXYNOS850_DRD_HSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int exynos850_usbdrd_phy_init(struct phy *phy)
|
||||||
|
{
|
||||||
|
struct phy_usb_instance *inst = phy_get_drvdata(phy);
|
||||||
|
struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(phy_drd->clk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* UTMI or PIPE3 specific init */
|
||||||
|
inst->phy_cfg->phy_init(phy_drd);
|
||||||
|
|
||||||
|
clk_disable_unprepare(phy_drd->clk);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int exynos850_usbdrd_phy_exit(struct phy *phy)
|
||||||
|
{
|
||||||
|
struct phy_usb_instance *inst = phy_get_drvdata(phy);
|
||||||
|
struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
|
||||||
|
void __iomem *regs_base = phy_drd->reg_phy;
|
||||||
|
u32 reg;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(phy_drd->clk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Set PHY clock and control HS PHY */
|
||||||
|
reg = readl(regs_base + EXYNOS850_DRD_UTMI);
|
||||||
|
reg &= ~(UTMI_DP_PULLDOWN | UTMI_DM_PULLDOWN);
|
||||||
|
reg |= UTMI_FORCE_SUSPEND | UTMI_FORCE_SLEEP;
|
||||||
|
writel(reg, regs_base + EXYNOS850_DRD_UTMI);
|
||||||
|
|
||||||
|
/* Power down PHY analog blocks */
|
||||||
|
reg = readl(regs_base + EXYNOS850_DRD_HSP_TEST);
|
||||||
|
reg |= HSP_TEST_SIDDQ;
|
||||||
|
writel(reg, regs_base + EXYNOS850_DRD_HSP_TEST);
|
||||||
|
|
||||||
|
/* Link reset */
|
||||||
|
reg = readl(regs_base + EXYNOS850_DRD_CLKRST);
|
||||||
|
reg |= CLKRST_LINK_SW_RST;
|
||||||
|
writel(reg, regs_base + EXYNOS850_DRD_CLKRST);
|
||||||
|
udelay(10); /* required before doing POR=low */
|
||||||
|
reg &= ~CLKRST_LINK_SW_RST;
|
||||||
|
writel(reg, regs_base + EXYNOS850_DRD_CLKRST);
|
||||||
|
|
||||||
|
clk_disable_unprepare(phy_drd->clk);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct phy_ops exynos850_usbdrd_phy_ops = {
|
||||||
|
.init = exynos850_usbdrd_phy_init,
|
||||||
|
.exit = exynos850_usbdrd_phy_exit,
|
||||||
|
.power_on = exynos5_usbdrd_phy_power_on,
|
||||||
|
.power_off = exynos5_usbdrd_phy_power_off,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
};
|
||||||
|
|
||||||
static int exynos5_usbdrd_phy_clk_handle(struct exynos5_usbdrd_phy *phy_drd)
|
static int exynos5_usbdrd_phy_clk_handle(struct exynos5_usbdrd_phy *phy_drd)
|
||||||
{
|
{
|
||||||
unsigned long ref_rate;
|
unsigned long ref_rate;
|
||||||
|
@ -782,6 +933,14 @@ static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct exynos5_usbdrd_phy_config phy_cfg_exynos850[] = {
|
||||||
|
{
|
||||||
|
.id = EXYNOS5_DRDPHY_UTMI,
|
||||||
|
.phy_isol = exynos5_usbdrd_phy_isol,
|
||||||
|
.phy_init = exynos850_usbdrd_utmi_init,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = {
|
static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = {
|
||||||
.phy_cfg = phy_cfg_exynos5,
|
.phy_cfg = phy_cfg_exynos5,
|
||||||
.phy_ops = &exynos5_usbdrd_phy_ops,
|
.phy_ops = &exynos5_usbdrd_phy_ops,
|
||||||
|
@ -812,6 +971,13 @@ static const struct exynos5_usbdrd_phy_drvdata exynos7_usbdrd_phy = {
|
||||||
.has_common_clk_gate = false,
|
.has_common_clk_gate = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct exynos5_usbdrd_phy_drvdata exynos850_usbdrd_phy = {
|
||||||
|
.phy_cfg = phy_cfg_exynos850,
|
||||||
|
.phy_ops = &exynos850_usbdrd_phy_ops,
|
||||||
|
.pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
|
||||||
|
.has_common_clk_gate = true,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct of_device_id exynos5_usbdrd_phy_of_match[] = {
|
static const struct of_device_id exynos5_usbdrd_phy_of_match[] = {
|
||||||
{
|
{
|
||||||
.compatible = "samsung,exynos5250-usbdrd-phy",
|
.compatible = "samsung,exynos5250-usbdrd-phy",
|
||||||
|
@ -825,6 +991,9 @@ static const struct of_device_id exynos5_usbdrd_phy_of_match[] = {
|
||||||
}, {
|
}, {
|
||||||
.compatible = "samsung,exynos7-usbdrd-phy",
|
.compatible = "samsung,exynos7-usbdrd-phy",
|
||||||
.data = &exynos7_usbdrd_phy
|
.data = &exynos7_usbdrd_phy
|
||||||
|
}, {
|
||||||
|
.compatible = "samsung,exynos850-usbdrd-phy",
|
||||||
|
.data = &exynos850_usbdrd_phy
|
||||||
},
|
},
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue