summaryrefslogtreecommitdiff
path: root/drivers/phy
diff options
context:
space:
mode:
authorKamil Debski <k.debski@samsung.com>2014-06-24 14:04:54 +0200
committerChanho Park <chanho61.park@samsung.com>2014-11-18 11:59:53 +0900
commitb8e91f4ad89b5f68ec852f004b71ae1b366430e0 (patch)
tree9984c41cbfb63816481d0f5508e8a0b1bd3a4de1 /drivers/phy
parent6732efcfe5365e7aa669e825e040a68137cb3269 (diff)
downloadlinux-3.10-b8e91f4ad89b5f68ec852f004b71ae1b366430e0.tar.gz
linux-3.10-b8e91f4ad89b5f68ec852f004b71ae1b366430e0.tar.bz2
linux-3.10-b8e91f4ad89b5f68ec852f004b71ae1b366430e0.zip
phy: phy-samsung-usb2: Change phy power on/power off sequence
The Exynos4412 USB 2.0 PHY hardware differs from the description provided in the documentation. Some register bits have different function. This patch fixes the defines of register bits and changes the way how phys are powered on and off. Signed-off-by: Kamil Debski <k.debski@samsung.com> Change-Id: I05f2568157c3d857b71d47c0e6506b35bf969699
Diffstat (limited to 'drivers/phy')
-rw-r--r--drivers/phy/phy-exynos4x12-usb2.c122
-rw-r--r--drivers/phy/phy-samsung-usb2.h3
2 files changed, 72 insertions, 53 deletions
diff --git a/drivers/phy/phy-exynos4x12-usb2.c b/drivers/phy/phy-exynos4x12-usb2.c
index 431568b5cc6..59d8dd3ff39 100644
--- a/drivers/phy/phy-exynos4x12-usb2.c
+++ b/drivers/phy/phy-exynos4x12-usb2.c
@@ -86,21 +86,23 @@
#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
@@ -196,6 +198,7 @@ static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst)
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);
}
@@ -206,42 +209,31 @@ static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
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);
@@ -262,52 +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_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;
}
diff --git a/drivers/phy/phy-samsung-usb2.h b/drivers/phy/phy-samsung-usb2.h
index 848744ab8f3..1cbdbf502a1 100644
--- a/drivers/phy/phy-samsung-usb2.h
+++ b/drivers/phy/phy-samsung-usb2.h
@@ -29,7 +29,8 @@ struct samsung_usb2_phy_instance {
const struct samsung_usb2_common_phy *cfg;
struct phy *phy;
struct samsung_usb2_phy_driver *drv;
- int enabled;
+ int int_cnt;
+ int ext_cnt;
};
struct samsung_usb2_phy_driver {