2 * Samsung SoC USB 1.1/2.0 PHY driver - S5PV210 support
4 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
5 * Authors: Kamil Debski <k.debski@samsung.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
12 #include <linux/delay.h>
14 #include <linux/phy/phy.h>
15 #include "phy-samsung-usb2.h"
17 /* Exynos USB PHY registers */
19 /* PHY power control */
20 #define S5PV210_UPHYPWR 0x0
22 #define S5PV210_UPHYPWR_PHY0_SUSPEND BIT(0)
23 #define S5PV210_UPHYPWR_PHY0_PWR BIT(3)
24 #define S5PV210_UPHYPWR_PHY0_OTG_PWR BIT(4)
25 #define S5PV210_UPHYPWR_PHY0 ( \
26 S5PV210_UPHYPWR_PHY0_SUSPEND | \
27 S5PV210_UPHYPWR_PHY0_PWR | \
28 S5PV210_UPHYPWR_PHY0_OTG_PWR)
30 #define S5PV210_UPHYPWR_PHY1_SUSPEND BIT(6)
31 #define S5PV210_UPHYPWR_PHY1_PWR BIT(7)
32 #define S5PV210_UPHYPWR_PHY1 ( \
33 S5PV210_UPHYPWR_PHY1_SUSPEND | \
34 S5PV210_UPHYPWR_PHY1_PWR)
36 /* PHY clock control */
37 #define S5PV210_UPHYCLK 0x4
39 #define S5PV210_UPHYCLK_PHYFSEL_MASK (0x3 << 0)
40 #define S5PV210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0)
41 #define S5PV210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0)
42 #define S5PV210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0)
44 #define S5PV210_UPHYCLK_PHY0_ID_PULLUP BIT(2)
45 #define S5PV210_UPHYCLK_PHY0_COMMON_ON BIT(4)
46 #define S5PV210_UPHYCLK_PHY1_COMMON_ON BIT(7)
48 /* PHY reset control */
49 #define S5PV210_UPHYRST 0x8
51 #define S5PV210_URSTCON_PHY0 BIT(0)
52 #define S5PV210_URSTCON_OTG_HLINK BIT(1)
53 #define S5PV210_URSTCON_OTG_PHYLINK BIT(2)
54 #define S5PV210_URSTCON_PHY1_ALL BIT(3)
55 #define S5PV210_URSTCON_HOST_LINK_ALL BIT(4)
57 /* Isolation, configured in the power management unit */
58 #define S5PV210_USB_ISOL_OFFSET 0x680c
59 #define S5PV210_USB_ISOL_DEVICE BIT(0)
60 #define S5PV210_USB_ISOL_HOST BIT(1)
70 * s5pv210_rate_to_clk() converts the supplied clock rate to the value that
71 * can be written to the phy register.
73 static int s5pv210_rate_to_clk(unsigned long rate, u32 *reg)
77 *reg = S5PV210_UPHYCLK_PHYFSEL_12MHZ;
80 *reg = S5PV210_UPHYCLK_PHYFSEL_24MHZ;
83 *reg = S5PV210_UPHYCLK_PHYFSEL_48MHZ;
92 static void s5pv210_isol(struct samsung_usb2_phy_instance *inst, bool on)
94 struct samsung_usb2_phy_driver *drv = inst->drv;
97 switch (inst->cfg->id) {
99 mask = S5PV210_USB_ISOL_DEVICE;
102 mask = S5PV210_USB_ISOL_HOST;
108 regmap_update_bits(drv->reg_pmu, S5PV210_USB_ISOL_OFFSET,
109 mask, on ? 0 : mask);
112 static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
114 struct samsung_usb2_phy_driver *drv = inst->drv;
120 switch (inst->cfg->id) {
122 phypwr = S5PV210_UPHYPWR_PHY0;
123 rstbits = S5PV210_URSTCON_PHY0;
126 phypwr = S5PV210_UPHYPWR_PHY1;
127 rstbits = S5PV210_URSTCON_PHY1_ALL |
128 S5PV210_URSTCON_HOST_LINK_ALL;
133 writel(drv->ref_reg_val, drv->reg_phy + S5PV210_UPHYCLK);
135 pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
137 writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
139 rst = readl(drv->reg_phy + S5PV210_UPHYRST);
141 writel(rst, drv->reg_phy + S5PV210_UPHYRST);
144 writel(rst, drv->reg_phy + S5PV210_UPHYRST);
145 /* The following delay is necessary for the reset sequence to be
150 pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
152 writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
156 static int s5pv210_power_on(struct samsung_usb2_phy_instance *inst)
158 s5pv210_isol(inst, 0);
159 s5pv210_phy_pwr(inst, 1);
164 static int s5pv210_power_off(struct samsung_usb2_phy_instance *inst)
166 s5pv210_phy_pwr(inst, 0);
167 s5pv210_isol(inst, 1);
172 static const struct samsung_usb2_common_phy s5pv210_phys[S5PV210_NUM_PHYS] = {
175 .id = S5PV210_DEVICE,
176 .power_on = s5pv210_power_on,
177 .power_off = s5pv210_power_off,
182 .power_on = s5pv210_power_on,
183 .power_off = s5pv210_power_off,
187 const struct samsung_usb2_phy_config s5pv210_usb2_phy_config = {
188 .num_phys = ARRAY_SIZE(s5pv210_phys),
189 .phys = s5pv210_phys,
190 .rate_to_clk = s5pv210_rate_to_clk,