#define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1)
#define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2)
#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)
-/*
- * According to Exynos 4x12 reference manual the values for
- * EXYNOS_4x12_URSTCON_HSIC are:
- * URSTCON_HSIC0 = BIT(5)
- * URSTCON_HSIC1 = BIT(6)
- * but from experiments with real hardware the above 2 bitfields
- * seems to be swapped, so define them to match the actual
- * hardware */
-#define EXYNOS_4x12_URSTCON_HSIC1 BIT(5)
#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_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_P2 BIT(10)
+#define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(8)
/* Isolation, configured in the power management unit */
#define EXYNOS_4x12_USB_ISOL_OFFSET 0x704
clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK);
clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK;
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);
}
u32 phypwr = 0;
u32 rst;
u32 pwr;
- u32 mode = 0;
- u32 switch_mode = 0;
switch (inst->cfg->id) {
case EXYNOS4x12_DEVICE:
phypwr = EXYNOS_4x12_UPHYPWR_PHY0;
rstbits = EXYNOS_4x12_URSTCON_PHY0;
- mode = EXYNOS_4x12_MODE_SWITCH_DEVICE;
- switch_mode = 1;
break;
case EXYNOS4x12_HOST:
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;
case EXYNOS4x12_HSIC0:
phypwr = EXYNOS_4x12_UPHYPWR_HSIC0;
rstbits = EXYNOS_4x12_URSTCON_HSIC0 |
- EXYNOS_4x12_URSTCON_HOST_LINK_P0 |
- EXYNOS_4x12_URSTCON_HOST_PHY;
+ EXYNOS_4x12_URSTCON_HOST_LINK_P1 ;
break;
case EXYNOS4x12_HSIC1:
phypwr = EXYNOS_4x12_UPHYPWR_HSIC1;
rstbits = EXYNOS_4x12_URSTCON_HSIC1 |
- EXYNOS_4x12_URSTCON_HOST_LINK_P1 |
- EXYNOS_4x12_URSTCON_HOST_PHY;
+ EXYNOS_4x12_URSTCON_HOST_LINK_P1;
break;
};
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 &= ~phypwr;
writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
}
}
-static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst)
+static void exynos4x12_power_on_internal(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++ > 0)
+ return;
- inst->enabled++;
exynos4x12_setup_clk(inst);
- exynos4x12_phy_pwr(inst, 1);
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;
+
+ 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_internal(&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);
- /* Power on the device, as it is necessary for HSIC to work */
if (inst->cfg->id == EXYNOS4x12_HSIC0 ||
- inst->cfg->id == EXYNOS4x12_HSIC1) {
- if (!device->enabled) {
- exynos4x12_phy_pwr(device, 1);
- exynos4x12_isol(device, 0);
- }
- device->enabled++;
+ inst->cfg->id == EXYNOS4x12_HSIC1) {
+ exynos4x12_power_on_internal(&drv->instances[EXYNOS4x12_DEVICE]);
+ exynos4x12_power_on_internal(&drv->instances[EXYNOS4x12_HOST]);
}
+ exynos4x12_power_on_internal(inst);
+
return 0;
}
+static void exynos4x12_power_off_internal(struct samsung_usb2_phy_instance *inst)
+{
+ if (inst->int_cnt-- > 1)
+ return;
+
+ exynos4x12_isol(inst, 1);
+ exynos4x12_phy_pwr(inst, 0);
+}
+
static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
- struct samsung_usb2_phy_instance *device =
- &drv->instances[EXYNOS4x12_DEVICE];
- inst->enabled--;
- if (inst->enabled)
+ if (inst->ext_cnt-- > 1)
return 0;
- exynos4x12_isol(inst, 1);
- exynos4x12_phy_pwr(inst, 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_internal(&drv->instances[EXYNOS4x12_DEVICE]);
if (inst->cfg->id == EXYNOS4x12_HSIC0 ||
- inst->cfg->id == EXYNOS4x12_HSIC1) {
- device->enabled--;
- if (!device->enabled) {
- exynos4x12_isol(device, 1);
- exynos4x12_phy_pwr(device, 0);
- }
+ inst->cfg->id == EXYNOS4x12_HSIC1) {
+ exynos4x12_power_off_internal(&drv->instances[EXYNOS4x12_DEVICE]);
+ exynos4x12_power_off_internal(&drv->instances[EXYNOS4x12_HOST]);
}
+ exynos4x12_power_off_internal(inst);
+
return 0;
}