summaryrefslogtreecommitdiff
path: root/drivers/phy
diff options
context:
space:
mode:
authorJagan Teki <jagan@amarulasolutions.com>2018-05-07 13:03:30 +0530
committerJagan Teki <jagan@amarulasolutions.com>2018-05-28 16:40:43 +0530
commit5f646bf1d7d0b22eefec3c22d5c9c94c6eb79228 (patch)
treee2924375f365069679ecb292933d9a426c662164 /drivers/phy
parentbafe5e3061497a52023b24b50b1104f0da880fed (diff)
phy: sun4i-usb: Add A83T USB PHY config
Unlike, other Allwinner SUN4I Phy supporting SOC, A83T has 2 USB PHY's and second one is HSIC. So phy control need to configure to handle these HSIC and SIDDQ requirement. Signed-off-by: Jagan Teki <jagan@amarulasolutions.com> Acked-by: Jun Nie <jun.nie@linaro.org>
Diffstat (limited to 'drivers/phy')
-rw-r--r--drivers/phy/allwinner/phy-sun4i-usb.c85
1 files changed, 68 insertions, 17 deletions
diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c
index de0a59a32c..01782ac97c 100644
--- a/drivers/phy/allwinner/phy-sun4i-usb.c
+++ b/drivers/phy/allwinner/phy-sun4i-usb.c
@@ -53,9 +53,19 @@
#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8)
#define SUNXI_ULPI_BYPASS_EN BIT(0)
+/* A83T specific control bits for PHY0 */
+#define PHY_CTL_VBUSVLDEXT BIT(5)
+#define PHY_CTL_SIDDQ BIT(3)
+
+/* A83T specific control bits for PHY2 HSIC */
+#define SUNXI_EHCI_HS_FORCE BIT(20)
+#define SUNXI_HSIC_CONNECT_INT BIT(16)
+#define SUNXI_HSIC BIT(1)
+
#define MAX_PHYS 4
enum sun4i_usb_phy_type {
+ sun8i_a83t_phy,
sun8i_h3_phy,
sun8i_v3s_phy,
sun50i_a64_phy,
@@ -92,13 +102,20 @@ struct sun4i_usb_phy_info {
.gpio_vbus = CONFIG_USB2_VBUS_PIN,
.gpio_vbus_det = NULL,
.gpio_id_det = NULL,
+#ifdef CONFIG_MACH_SUN8I_A83T
+ .rst_mask = (CCM_USB_CTRL_HSIC_RST | CCM_USB_CTRL_HSIC_CLK |
+ CCM_USB_CTRL_12M_CLK),
+#else
.rst_mask = (CCM_USB_CTRL_PHY2_RST | CCM_USB_CTRL_PHY2_CLK),
+#endif
},
{
.gpio_vbus = CONFIG_USB3_VBUS_PIN,
.gpio_vbus_det = NULL,
.gpio_id_det = NULL,
+#ifdef CONFIG_MACH_SUN6I
.rst_mask = (CCM_USB_CTRL_PHY3_RST | CCM_USB_CTRL_PHY3_CLK),
+#endif
},
};
@@ -166,9 +183,10 @@ static void sun4i_usb_phy_write(struct phy *phy, u32 addr, u32 data, int len)
}
}
-static void sun4i_usb_phy_passby(struct sun4i_usb_phy_plat *usb_phy,
- bool enable)
+static void sun4i_usb_phy_passby(struct phy *phy, bool enable)
{
+ struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev);
+ struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id];
u32 bits, reg_value;
if (!usb_phy->pmu)
@@ -176,6 +194,12 @@ static void sun4i_usb_phy_passby(struct sun4i_usb_phy_plat *usb_phy,
bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN |
SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN;
+
+ /* A83T USB2 is HSIC */
+ if (data->cfg->type == sun8i_a83t_phy && usb_phy->id == 2)
+ bits |= SUNXI_EHCI_HS_FORCE | SUNXI_HSIC_CONNECT_INT |
+ SUNXI_HSIC;
+
reg_value = readl(usb_phy->pmu);
if (enable)
@@ -244,25 +268,36 @@ static int sun4i_usb_phy_init(struct phy *phy)
setbits_le32(&data->ccm->usb_clk_cfg, usb_phy->rst_mask);
- if (usb_phy->pmu && data->cfg->enable_pmu_unk1) {
- val = readl(usb_phy->pmu + REG_PMU_UNK1);
- writel(val & ~2, usb_phy->pmu + REG_PMU_UNK1);
- }
+ if (data->cfg->type == sun8i_a83t_phy) {
+ if (phy->id == 0) {
+ val = readl(data->base + data->cfg->phyctl_offset);
+ val |= PHY_CTL_VBUSVLDEXT;
+ val &= ~PHY_CTL_SIDDQ;
+ writel(val, data->base + data->cfg->phyctl_offset);
+ }
+ } else {
+ if (usb_phy->pmu && data->cfg->enable_pmu_unk1) {
+ val = readl(usb_phy->pmu + REG_PMU_UNK1);
+ writel(val & ~2, usb_phy->pmu + REG_PMU_UNK1);
+ }
- if (usb_phy->id == 0)
- sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, PHY_RES45_CAL_DATA,
- PHY_RES45_CAL_LEN);
+ if (usb_phy->id == 0)
+ sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN,
+ PHY_RES45_CAL_DATA,
+ PHY_RES45_CAL_LEN);
- /* Adjust PHY's magnitude and rate */
- sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, PHY_TX_MAGNITUDE |
- PHY_TX_RATE, PHY_TX_AMPLITUDE_LEN);
+ /* Adjust PHY's magnitude and rate */
+ sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE,
+ PHY_TX_MAGNITUDE | PHY_TX_RATE,
+ PHY_TX_AMPLITUDE_LEN);
- /* Disconnect threshold adjustment */
- sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, data->cfg->disc_thresh,
- PHY_DISCON_TH_LEN);
+ /* Disconnect threshold adjustment */
+ sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL,
+ data->cfg->disc_thresh, PHY_DISCON_TH_LEN);
+ }
if (usb_phy->id != 0)
- sun4i_usb_phy_passby(usb_phy, true);
+ sun4i_usb_phy_passby(phy, true);
sun4i_usb_phy0_reroute(data, true);
@@ -274,7 +309,16 @@ static int sun4i_usb_phy_exit(struct phy *phy)
struct sun4i_usb_phy_data *data = dev_get_priv(phy->dev);
struct sun4i_usb_phy_plat *usb_phy = &data->usb_phy[phy->id];
- sun4i_usb_phy_passby(usb_phy, false);
+ if (phy->id == 0) {
+ if (data->cfg->type == sun8i_a83t_phy) {
+ void __iomem *phyctl = data->base +
+ data->cfg->phyctl_offset;
+
+ writel(readl(phyctl) | PHY_CTL_SIDDQ, phyctl);
+ }
+ }
+
+ sun4i_usb_phy_passby(phy, false);
clrbits_le32(&data->ccm->usb_clk_cfg, usb_phy->rst_mask);
@@ -416,6 +460,12 @@ static int sun4i_usb_phy_probe(struct udevice *dev)
return 0;
}
+static const struct sun4i_usb_phy_cfg sun8i_a83t_cfg = {
+ .num_phys = 3,
+ .type = sun8i_a83t_phy,
+ .phyctl_offset = REG_PHYCTL_A33,
+};
+
static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = {
.num_phys = 4,
.type = sun8i_h3_phy,
@@ -444,6 +494,7 @@ static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = {
};
static const struct udevice_id sun4i_usb_phy_ids[] = {
+ { .compatible = "allwinner,sun8i-a83t-usb-phy", .data = (ulong)&sun8i_a83t_cfg },
{ .compatible = "allwinner,sun8i-h3-usb-phy", .data = (ulong)&sun8i_h3_cfg },
{ .compatible = "allwinner,sun8i-v3s-usb-phy", .data = (ulong)&sun8i_v3s_cfg },
{ .compatible = "allwinner,sun50i-a64-usb-phy", .data = (ulong)&sun50i_a64_cfg},