This patch adds host phy support for Samsung's Exynos4412 SoC to samsung-usbphy driver. This patch is created upon "http://git.kernel.org/?p=linux/kernel/git/balbi/usb.git;a=commit;h=2564b526b8cf01e6c36285edfd40a438e683c2b8" Cc: Praveen Paneri <p.paneri@xxxxxxxxxxx> Signed-off-by: Dongjin Kim <tobetter@xxxxxxxxx> --- drivers/usb/phy/samsung-usbphy.c | 156 +++++++++++++++++++++++++++++++++++++- 1 file changed, 154 insertions(+), 2 deletions(-) diff --git a/drivers/usb/phy/samsung-usbphy.c b/drivers/usb/phy/samsung-usbphy.c index 6ea5537..c800fa4 100644 --- a/drivers/usb/phy/samsung-usbphy.c +++ b/drivers/usb/phy/samsung-usbphy.c @@ -47,7 +47,7 @@ #define PHYCLK_MODE_USB11 (0x1 << 6) #define PHYCLK_EXT_OSC (0x1 << 5) -#define PHYCLK_COMMON_ON_N (0x1 << 4) +#define PHYCLK_COMMON_ON_N_PHY0 (0x1 << 4) #define PHYCLK_ID_PULL (0x1 << 2) #define PHYCLK_CLKSEL_MASK (0x3 << 0) #define PHYCLK_CLKSEL_48M (0x0 << 0) @@ -60,6 +60,22 @@ #define RSTCON_HLINK_SWRST (0x1 << 1) #define RSTCON_SWRST (0x1 << 0) +/* For Exynos4412 */ +#define PHYCLK_COMMON_ON_N_PHY1 (0x1 << 7) + +#define PHYPWR_NORMAL_MASK_HSIC1 (0x7 << 12) +#define PHYPWR_NORMAL_MASK_HSIC0 (0x7 << 9) +#define PHYPWR_NORMAL_MASK_PHY1 (0x7 << 6) + +#define PHYPWR_ANALOG_POWERDOWN_PHY1 (0x1 << 7) + +#define RSTCON_HLINK_SWRST_MASK (0xf << 7) +#define RSTCON_PHY1_SWRST_MASK (0xf << 3) +#define RSTCON_PHY0_SWRST_MASK (0x7 << 0) + +#define EXYNOS4_PHY_HSIC_CTRL0 (0x04) +#define EXYNOS4_PHY_HSIC_CTRL1 (0x08) + /* EXYNOS5 */ #define EXYNOS5_PHY_HOST_CTRL0 (0x00) @@ -174,6 +190,7 @@ enum samsung_cpu_type { TYPE_S3C64XX, TYPE_EXYNOS4210, + TYPE_EXYNOS4412, TYPE_EXYNOS5250, }; @@ -322,6 +339,17 @@ static void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) en_mask = sphy->drv_data->hostphy_en_mask; } break; + case TYPE_EXYNOS4412: + if (sphy->phy_type == USB_PHY_TYPE_DEVICE) { + reg = sphy->pmuregs + + sphy->drv_data->devphy_reg_offset; + en_mask = sphy->drv_data->devphy_en_mask; + } else if (sphy->phy_type == USB_PHY_TYPE_HOST) { + reg = sphy->pmuregs + + sphy->drv_data->hostphy_reg_offset; + en_mask = sphy->drv_data->hostphy_en_mask; + } + break; default: dev_err(sphy->dev, "Invalid SoC type\n"); return; @@ -422,6 +450,29 @@ static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) refclk_freq = FSEL_CLKSEL_24M; break; } + } else if (sphy->drv_data->cpu_type == TYPE_EXYNOS4412) { + switch (clk_get_rate(ref_clk)) { + case 9600 * KHZ: + refclk_freq = FSEL_CLKSEL_9600K; + break; + case 10 * MHZ: + refclk_freq = FSEL_CLKSEL_10M; + break; + case 12 * MHZ: + refclk_freq = FSEL_CLKSEL_12M; + break; + case 19200 * KHZ: + refclk_freq = FSEL_CLKSEL_19200K; + break; + case 20 * MHZ: + refclk_freq = FSEL_CLKSEL_20M; + break; + case 24 * MHZ: + default: + /* default reference clock */ + refclk_freq = FSEL_CLKSEL_24M; + break; + } } else { switch (clk_get_rate(ref_clk)) { case 12 * MHZ: @@ -561,6 +612,69 @@ static void samsung_exynos5_usbphy_enable(struct samsung_usbphy *sphy) writel(ohcictrl, regs + EXYNOS5_PHY_HOST_OHCICTRL); } +static bool exynos4_phyhost_is_on(void *regs) +{ + u32 reg; + + reg = readl(regs + SAMSUNG_PHYPWR); + + return !(reg & PHYPWR_ANALOG_POWERDOWN_PHY1); +} + +static void samsung_exynos4412_usbphy_enable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phypwr; + u32 phyclk; + u32 rstcon; + + /* + * phy_usage helps in keeping usage count for phy + * so that the first consumer enabling the phy is also + * the last consumer to disable it. + */ + + atomic_inc(&sphy->phy_usage); + + if (exynos4_phyhost_is_on(regs)) { + dev_info(sphy->dev, "Already power on PHY\n"); + return; + } + + writel(EXYNOS_USBPHY_ENABLE, sphy->pmuregs + EXYNOS4_PHY_HSIC_CTRL0); + writel(EXYNOS_USBPHY_ENABLE, sphy->pmuregs + EXYNOS4_PHY_HSIC_CTRL1); + + /* Common block configuration during suspend */ + phyclk = sphy->ref_clk_freq + & ~(PHYCLK_COMMON_ON_N_PHY0 | PHYCLK_COMMON_ON_N_PHY1); + writel(phyclk, regs + SAMSUNG_PHYCLK); + + /* set to normal of Device */ + phypwr = readl(regs + SAMSUNG_PHYPWR) & ~PHYPWR_NORMAL_MASK_PHY0; + writel(phypwr, regs + SAMSUNG_PHYPWR); + + /* set to normal of Host */ + phypwr &= ~(PHYPWR_NORMAL_MASK_HSIC0 | PHYPWR_NORMAL_MASK_HSIC1 + | PHYPWR_NORMAL_MASK_PHY1); + writel(phypwr, regs + SAMSUNG_PHYPWR); + + /* reset both PHY and Link of Device */ + rstcon = readl(regs + SAMSUNG_RSTCON) | RSTCON_PHY0_SWRST_MASK; + writel(rstcon, regs + SAMSUNG_RSTCON); + udelay(10); + rstcon &= ~RSTCON_PHY0_SWRST_MASK; + writel(rstcon, regs + SAMSUNG_RSTCON); + + /* reset both PHY and Link of Host */ + rstcon = readl(regs + SAMSUNG_RSTCON) + | (RSTCON_HLINK_SWRST_MASK | RSTCON_PHY1_SWRST_MASK); + writel(rstcon, regs + SAMSUNG_RSTCON); + udelay(10); + rstcon &= ~(RSTCON_HLINK_SWRST_MASK | RSTCON_PHY1_SWRST_MASK); + writel(rstcon, regs + SAMSUNG_RSTCON); + udelay(80); +} + static void samsung_usbphy_enable(struct samsung_usbphy *sphy) { void __iomem *regs = sphy->regs; @@ -575,7 +689,7 @@ static void samsung_usbphy_enable(struct samsung_usbphy *sphy) switch (sphy->drv_data->cpu_type) { case TYPE_S3C64XX: - phyclk &= ~PHYCLK_COMMON_ON_N; + phyclk &= ~PHYCLK_COMMON_ON_N_PHY0; phypwr &= ~PHYPWR_NORMAL_MASK; rstcon |= RSTCON_SWRST; break; @@ -631,6 +745,28 @@ static void samsung_exynos5_usbphy_disable(struct samsung_usbphy *sphy) writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); } +static void samsung_exynos4412_usbphy_disable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phypwr; + + if (atomic_dec_return(&sphy->phy_usage) > 0) { + dev_info(sphy->dev, "still being used\n"); + return; + } + + /* unset to normal of Host and Device */ + phypwr = readl(regs + SAMSUNG_PHYPWR); + phypwr |= (PHYPWR_NORMAL_MASK_HSIC0 + | PHYPWR_NORMAL_MASK_HSIC1 + | PHYPWR_NORMAL_MASK_PHY1 + | PHYPWR_NORMAL_MASK_PHY0); + writel(phypwr, regs + SAMSUNG_PHYPWR); + + writel(0, sphy->pmuregs + EXYNOS4_PHY_HSIC_CTRL0); + writel(0, sphy->pmuregs + EXYNOS4_PHY_HSIC_CTRL1); +} + static void samsung_usbphy_disable(struct samsung_usbphy *sphy) { void __iomem *regs = sphy->regs; @@ -696,6 +832,8 @@ static int samsung_usbphy_init(struct usb_phy *phy) /* Initialize usb phy registers */ if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) samsung_exynos5_usbphy_enable(sphy); + else if (sphy->drv_data->cpu_type == TYPE_EXYNOS4412) + samsung_exynos4412_usbphy_enable(sphy); else samsung_usbphy_enable(sphy); @@ -739,6 +877,8 @@ static void samsung_usbphy_shutdown(struct usb_phy *phy) /* De-initialize usb phy registers */ if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) samsung_exynos5_usbphy_disable(sphy); + else if (sphy->drv_data->cpu_type == TYPE_EXYNOS4412) + samsung_exynos4412_usbphy_disable(sphy); else samsung_usbphy_disable(sphy); @@ -872,6 +1012,12 @@ static const struct samsung_usbphy_drvdata usbphy_exynos4 = { .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, }; +static const struct samsung_usbphy_drvdata usbphy_exynos4412 = { + .cpu_type = TYPE_EXYNOS4412, + .devphy_en_mask = EXYNOS_USBPHY_ENABLE, + .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, +}; + static struct samsung_usbphy_drvdata usbphy_exynos5 = { .cpu_type = TYPE_EXYNOS5250, .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, @@ -887,6 +1033,9 @@ static const struct of_device_id samsung_usbphy_dt_match[] = { .compatible = "samsung,exynos4210-usbphy", .data = &usbphy_exynos4, }, { + .compatible = "samsung,exynos4412-usbphy", + .data = &usbphy_exynos4412, + }, { .compatible = "samsung,exynos5250-usbphy", .data = &usbphy_exynos5 }, @@ -903,6 +1052,9 @@ static struct platform_device_id samsung_usbphy_driver_ids[] = { .name = "exynos4210-usbphy", .driver_data = (unsigned long)&usbphy_exynos4, }, { + .name = "exynos4412-usbphy", + .driver_data = (unsigned long)&usbphy_exynos4412, + }, { .name = "exynos5250-usbphy", .driver_data = (unsigned long)&usbphy_exynos5, }, -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html