|
@@ -86,13 +86,23 @@
|
|
#define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1)
|
|
#define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1)
|
|
#define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2)
|
|
#define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2)
|
|
#define EXYNOS_4x12_URSTCON_HOST_PHY BIT(3)
|
|
#define EXYNOS_4x12_URSTCON_HOST_PHY BIT(3)
|
|
|
|
+/* The following bit defines are presented in the
|
|
|
|
+ * order taken from the Exynos4412 reference manual.
|
|
|
|
+ *
|
|
|
|
+ * During experiments with the hardware and debugging
|
|
|
|
+ * it was determined that the hardware behaves contrary
|
|
|
|
+ * to the manual.
|
|
|
|
+ *
|
|
|
|
+ * The following bit values were chaned accordingly to the
|
|
|
|
+ * results of real hardware experiments.
|
|
|
|
+ */
|
|
#define EXYNOS_4x12_URSTCON_PHY1 BIT(4)
|
|
#define EXYNOS_4x12_URSTCON_PHY1 BIT(4)
|
|
-#define EXYNOS_4x12_URSTCON_HSIC0 BIT(5)
|
|
|
|
-#define EXYNOS_4x12_URSTCON_HSIC1 BIT(6)
|
|
|
|
|
|
+#define EXYNOS_4x12_URSTCON_HSIC0 BIT(6)
|
|
|
|
+#define EXYNOS_4x12_URSTCON_HSIC1 BIT(5)
|
|
#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL BIT(7)
|
|
#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL BIT(7)
|
|
-#define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(8)
|
|
|
|
|
|
+#define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(10)
|
|
#define EXYNOS_4x12_URSTCON_HOST_LINK_P1 BIT(9)
|
|
#define EXYNOS_4x12_URSTCON_HOST_LINK_P1 BIT(9)
|
|
-#define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(10)
|
|
|
|
|
|
+#define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(8)
|
|
|
|
|
|
/* Isolation, configured in the power management unit */
|
|
/* Isolation, configured in the power management unit */
|
|
#define EXYNOS_4x12_USB_ISOL_OFFSET 0x704
|
|
#define EXYNOS_4x12_USB_ISOL_OFFSET 0x704
|
|
@@ -188,6 +198,7 @@ static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst)
|
|
clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK);
|
|
clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK);
|
|
clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK;
|
|
clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK;
|
|
clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET;
|
|
clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET;
|
|
|
|
+ clk |= EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON;
|
|
writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK);
|
|
writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -198,27 +209,22 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
|
|
u32 phypwr = 0;
|
|
u32 phypwr = 0;
|
|
u32 rst;
|
|
u32 rst;
|
|
u32 pwr;
|
|
u32 pwr;
|
|
- u32 mode = 0;
|
|
|
|
- u32 switch_mode = 0;
|
|
|
|
|
|
|
|
switch (inst->cfg->id) {
|
|
switch (inst->cfg->id) {
|
|
case EXYNOS4x12_DEVICE:
|
|
case EXYNOS4x12_DEVICE:
|
|
phypwr = EXYNOS_4x12_UPHYPWR_PHY0;
|
|
phypwr = EXYNOS_4x12_UPHYPWR_PHY0;
|
|
rstbits = EXYNOS_4x12_URSTCON_PHY0;
|
|
rstbits = EXYNOS_4x12_URSTCON_PHY0;
|
|
- mode = EXYNOS_4x12_MODE_SWITCH_DEVICE;
|
|
|
|
- switch_mode = 1;
|
|
|
|
break;
|
|
break;
|
|
case EXYNOS4x12_HOST:
|
|
case EXYNOS4x12_HOST:
|
|
phypwr = EXYNOS_4x12_UPHYPWR_PHY1;
|
|
phypwr = EXYNOS_4x12_UPHYPWR_PHY1;
|
|
- rstbits = EXYNOS_4x12_URSTCON_HOST_PHY;
|
|
|
|
- mode = EXYNOS_4x12_MODE_SWITCH_HOST;
|
|
|
|
- switch_mode = 1;
|
|
|
|
|
|
+ rstbits = EXYNOS_4x12_URSTCON_HOST_PHY |
|
|
|
|
+ EXYNOS_4x12_URSTCON_PHY1 |
|
|
|
|
+ EXYNOS_4x12_URSTCON_HOST_LINK_P0;
|
|
break;
|
|
break;
|
|
case EXYNOS4x12_HSIC0:
|
|
case EXYNOS4x12_HSIC0:
|
|
phypwr = EXYNOS_4x12_UPHYPWR_HSIC0;
|
|
phypwr = EXYNOS_4x12_UPHYPWR_HSIC0;
|
|
- rstbits = EXYNOS_4x12_URSTCON_HSIC1 |
|
|
|
|
- EXYNOS_4x12_URSTCON_HOST_LINK_P0 |
|
|
|
|
- EXYNOS_4x12_URSTCON_HOST_PHY;
|
|
|
|
|
|
+ rstbits = EXYNOS_4x12_URSTCON_HSIC0 |
|
|
|
|
+ EXYNOS_4x12_URSTCON_HOST_LINK_P1;
|
|
break;
|
|
break;
|
|
case EXYNOS4x12_HSIC1:
|
|
case EXYNOS4x12_HSIC1:
|
|
phypwr = EXYNOS_4x12_UPHYPWR_HSIC1;
|
|
phypwr = EXYNOS_4x12_UPHYPWR_HSIC1;
|
|
@@ -228,11 +234,6 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
|
|
};
|
|
};
|
|
|
|
|
|
if (on) {
|
|
if (on) {
|
|
- if (switch_mode)
|
|
|
|
- regmap_update_bits(drv->reg_sys,
|
|
|
|
- EXYNOS_4x12_MODE_SWITCH_OFFSET,
|
|
|
|
- EXYNOS_4x12_MODE_SWITCH_MASK, mode);
|
|
|
|
-
|
|
|
|
pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
|
|
pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
|
|
pwr &= ~phypwr;
|
|
pwr &= ~phypwr;
|
|
writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
|
|
writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
|
|
@@ -253,41 +254,78 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst)
|
|
|
|
|
|
+static void exynos4x12_power_on_int(struct samsung_usb2_phy_instance *inst)
|
|
{
|
|
{
|
|
- struct samsung_usb2_phy_driver *drv = inst->drv;
|
|
|
|
|
|
+ if (inst->int_cnt++ > 0)
|
|
|
|
+ return;
|
|
|
|
|
|
- inst->enabled = 1;
|
|
|
|
exynos4x12_setup_clk(inst);
|
|
exynos4x12_setup_clk(inst);
|
|
- exynos4x12_phy_pwr(inst, 1);
|
|
|
|
exynos4x12_isol(inst, 0);
|
|
exynos4x12_isol(inst, 0);
|
|
|
|
+ exynos4x12_phy_pwr(inst, 1);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst)
|
|
|
|
+{
|
|
|
|
+ struct samsung_usb2_phy_driver *drv = inst->drv;
|
|
|
|
+
|
|
|
|
+ if (inst->ext_cnt++ > 0)
|
|
|
|
+ return 0;
|
|
|
|
|
|
- /* Power on the device, as it is necessary for HSIC to work */
|
|
|
|
- if (inst->cfg->id == EXYNOS4x12_HSIC0) {
|
|
|
|
- struct samsung_usb2_phy_instance *device =
|
|
|
|
- &drv->instances[EXYNOS4x12_DEVICE];
|
|
|
|
- exynos4x12_phy_pwr(device, 1);
|
|
|
|
- exynos4x12_isol(device, 0);
|
|
|
|
|
|
+ if (inst->cfg->id == EXYNOS4x12_HOST) {
|
|
|
|
+ regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET,
|
|
|
|
+ EXYNOS_4x12_MODE_SWITCH_MASK,
|
|
|
|
+ EXYNOS_4x12_MODE_SWITCH_HOST);
|
|
|
|
+ exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (inst->cfg->id == EXYNOS4x12_DEVICE)
|
|
|
|
+ regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET,
|
|
|
|
+ EXYNOS_4x12_MODE_SWITCH_MASK,
|
|
|
|
+ EXYNOS_4x12_MODE_SWITCH_DEVICE);
|
|
|
|
+
|
|
|
|
+ if (inst->cfg->id == EXYNOS4x12_HSIC0 ||
|
|
|
|
+ inst->cfg->id == EXYNOS4x12_HSIC1) {
|
|
|
|
+ exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]);
|
|
|
|
+ exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_HOST]);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ exynos4x12_power_on_int(inst);
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst)
|
|
|
|
|
|
+static void exynos4x12_power_off_int(struct samsung_usb2_phy_instance *inst)
|
|
{
|
|
{
|
|
- struct samsung_usb2_phy_driver *drv = inst->drv;
|
|
|
|
- struct samsung_usb2_phy_instance *device =
|
|
|
|
- &drv->instances[EXYNOS4x12_DEVICE];
|
|
|
|
|
|
+ if (inst->int_cnt-- > 1)
|
|
|
|
+ return;
|
|
|
|
|
|
- inst->enabled = 0;
|
|
|
|
exynos4x12_isol(inst, 1);
|
|
exynos4x12_isol(inst, 1);
|
|
exynos4x12_phy_pwr(inst, 0);
|
|
exynos4x12_phy_pwr(inst, 0);
|
|
|
|
+}
|
|
|
|
|
|
- if (inst->cfg->id == EXYNOS4x12_HSIC0 && !device->enabled) {
|
|
|
|
- exynos4x12_isol(device, 1);
|
|
|
|
- exynos4x12_phy_pwr(device, 0);
|
|
|
|
|
|
+static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst)
|
|
|
|
+{
|
|
|
|
+ struct samsung_usb2_phy_driver *drv = inst->drv;
|
|
|
|
+
|
|
|
|
+ if (inst->ext_cnt-- > 1)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ if (inst->cfg->id == EXYNOS4x12_DEVICE)
|
|
|
|
+ regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET,
|
|
|
|
+ EXYNOS_4x12_MODE_SWITCH_MASK,
|
|
|
|
+ EXYNOS_4x12_MODE_SWITCH_HOST);
|
|
|
|
+
|
|
|
|
+ if (inst->cfg->id == EXYNOS4x12_HOST)
|
|
|
|
+ exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]);
|
|
|
|
+
|
|
|
|
+ if (inst->cfg->id == EXYNOS4x12_HSIC0 ||
|
|
|
|
+ inst->cfg->id == EXYNOS4x12_HSIC1) {
|
|
|
|
+ exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]);
|
|
|
|
+ exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_HOST]);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ exynos4x12_power_off_int(inst);
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|