0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/delay.h>
0010 #include <linux/io.h>
0011 #include <linux/phy/phy.h>
0012 #include <linux/regmap.h>
0013 #include "phy-samsung-usb2.h"
0014
0015
0016
0017
0018 #define EXYNOS_4210_UPHYPWR 0x0
0019
0020 #define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND BIT(0)
0021 #define EXYNOS_4210_UPHYPWR_PHY0_PWR BIT(3)
0022 #define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR BIT(4)
0023 #define EXYNOS_4210_UPHYPWR_PHY0_SLEEP BIT(5)
0024 #define EXYNOS_4210_UPHYPWR_PHY0 ( \
0025 EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \
0026 EXYNOS_4210_UPHYPWR_PHY0_PWR | \
0027 EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \
0028 EXYNOS_4210_UPHYPWR_PHY0_SLEEP)
0029
0030 #define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND BIT(6)
0031 #define EXYNOS_4210_UPHYPWR_PHY1_PWR BIT(7)
0032 #define EXYNOS_4210_UPHYPWR_PHY1_SLEEP BIT(8)
0033 #define EXYNOS_4210_UPHYPWR_PHY1 ( \
0034 EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \
0035 EXYNOS_4210_UPHYPWR_PHY1_PWR | \
0036 EXYNOS_4210_UPHYPWR_PHY1_SLEEP)
0037
0038 #define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND BIT(9)
0039 #define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP BIT(10)
0040 #define EXYNOS_4210_UPHYPWR_HSIC0 ( \
0041 EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \
0042 EXYNOS_4210_UPHYPWR_HSIC0_SLEEP)
0043
0044 #define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND BIT(11)
0045 #define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP BIT(12)
0046 #define EXYNOS_4210_UPHYPWR_HSIC1 ( \
0047 EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \
0048 EXYNOS_4210_UPHYPWR_HSIC1_SLEEP)
0049
0050
0051 #define EXYNOS_4210_UPHYCLK 0x4
0052
0053 #define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK (0x3 << 0)
0054 #define EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET 0
0055 #define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0)
0056 #define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0)
0057 #define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0)
0058
0059 #define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP BIT(2)
0060 #define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON BIT(4)
0061 #define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON BIT(7)
0062
0063
0064 #define EXYNOS_4210_UPHYRST 0x8
0065
0066 #define EXYNOS_4210_URSTCON_PHY0 BIT(0)
0067 #define EXYNOS_4210_URSTCON_OTG_HLINK BIT(1)
0068 #define EXYNOS_4210_URSTCON_OTG_PHYLINK BIT(2)
0069 #define EXYNOS_4210_URSTCON_PHY1_ALL BIT(3)
0070 #define EXYNOS_4210_URSTCON_PHY1_P0 BIT(4)
0071 #define EXYNOS_4210_URSTCON_PHY1_P1P2 BIT(5)
0072 #define EXYNOS_4210_URSTCON_HOST_LINK_ALL BIT(6)
0073 #define EXYNOS_4210_URSTCON_HOST_LINK_P0 BIT(7)
0074 #define EXYNOS_4210_URSTCON_HOST_LINK_P1 BIT(8)
0075 #define EXYNOS_4210_URSTCON_HOST_LINK_P2 BIT(9)
0076
0077
0078 #define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET 0x704
0079 #define EXYNOS_4210_USB_ISOL_DEVICE BIT(0)
0080 #define EXYNOS_4210_USB_ISOL_HOST_OFFSET 0x708
0081 #define EXYNOS_4210_USB_ISOL_HOST BIT(0)
0082
0083
0084 #define EXYNOS_4210_UPHY1CON 0x34
0085 #define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION 0x1
0086
0087
0088 #define EXYNOS_4210_MODE_SWITCH_OFFSET 0x21c
0089 #define EXYNOS_4210_MODE_SWITCH_MASK 1
0090 #define EXYNOS_4210_MODE_SWITCH_DEVICE 0
0091 #define EXYNOS_4210_MODE_SWITCH_HOST 1
0092
0093 enum exynos4210_phy_id {
0094 EXYNOS4210_DEVICE,
0095 EXYNOS4210_HOST,
0096 EXYNOS4210_HSIC0,
0097 EXYNOS4210_HSIC1,
0098 EXYNOS4210_NUM_PHYS,
0099 };
0100
0101
0102
0103
0104
0105 static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg)
0106 {
0107 switch (rate) {
0108 case 12 * MHZ:
0109 *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
0110 break;
0111 case 24 * MHZ:
0112 *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
0113 break;
0114 case 48 * MHZ:
0115 *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
0116 break;
0117 default:
0118 return -EINVAL;
0119 }
0120
0121 return 0;
0122 }
0123
0124 static void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on)
0125 {
0126 struct samsung_usb2_phy_driver *drv = inst->drv;
0127 u32 offset;
0128 u32 mask;
0129
0130 switch (inst->cfg->id) {
0131 case EXYNOS4210_DEVICE:
0132 offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET;
0133 mask = EXYNOS_4210_USB_ISOL_DEVICE;
0134 break;
0135 case EXYNOS4210_HOST:
0136 offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET;
0137 mask = EXYNOS_4210_USB_ISOL_HOST;
0138 break;
0139 default:
0140 return;
0141 }
0142
0143 regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
0144 }
0145
0146 static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
0147 {
0148 struct samsung_usb2_phy_driver *drv = inst->drv;
0149 u32 rstbits = 0;
0150 u32 phypwr = 0;
0151 u32 rst;
0152 u32 pwr;
0153 u32 clk;
0154
0155 switch (inst->cfg->id) {
0156 case EXYNOS4210_DEVICE:
0157 phypwr = EXYNOS_4210_UPHYPWR_PHY0;
0158 rstbits = EXYNOS_4210_URSTCON_PHY0;
0159 break;
0160 case EXYNOS4210_HOST:
0161 phypwr = EXYNOS_4210_UPHYPWR_PHY1;
0162 rstbits = EXYNOS_4210_URSTCON_PHY1_ALL |
0163 EXYNOS_4210_URSTCON_PHY1_P0 |
0164 EXYNOS_4210_URSTCON_PHY1_P1P2 |
0165 EXYNOS_4210_URSTCON_HOST_LINK_ALL |
0166 EXYNOS_4210_URSTCON_HOST_LINK_P0;
0167 writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON);
0168 break;
0169 case EXYNOS4210_HSIC0:
0170 phypwr = EXYNOS_4210_UPHYPWR_HSIC0;
0171 rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 |
0172 EXYNOS_4210_URSTCON_HOST_LINK_P1;
0173 break;
0174 case EXYNOS4210_HSIC1:
0175 phypwr = EXYNOS_4210_UPHYPWR_HSIC1;
0176 rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 |
0177 EXYNOS_4210_URSTCON_HOST_LINK_P2;
0178 break;
0179 }
0180
0181 if (on) {
0182 clk = readl(drv->reg_phy + EXYNOS_4210_UPHYCLK);
0183 clk &= ~EXYNOS_4210_UPHYCLK_PHYFSEL_MASK;
0184 clk |= drv->ref_reg_val << EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET;
0185 writel(clk, drv->reg_phy + EXYNOS_4210_UPHYCLK);
0186
0187 pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
0188 pwr &= ~phypwr;
0189 writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
0190
0191 rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST);
0192 rst |= rstbits;
0193 writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
0194 udelay(10);
0195 rst &= ~rstbits;
0196 writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
0197
0198
0199 udelay(80);
0200 } else {
0201 pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
0202 pwr |= phypwr;
0203 writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
0204 }
0205 }
0206
0207 static int exynos4210_power_on(struct samsung_usb2_phy_instance *inst)
0208 {
0209
0210 exynos4210_phy_pwr(inst, 1);
0211 exynos4210_isol(inst, 0);
0212
0213 return 0;
0214 }
0215
0216 static int exynos4210_power_off(struct samsung_usb2_phy_instance *inst)
0217 {
0218 exynos4210_isol(inst, 1);
0219 exynos4210_phy_pwr(inst, 0);
0220
0221 return 0;
0222 }
0223
0224
0225 static const struct samsung_usb2_common_phy exynos4210_phys[] = {
0226 {
0227 .label = "device",
0228 .id = EXYNOS4210_DEVICE,
0229 .power_on = exynos4210_power_on,
0230 .power_off = exynos4210_power_off,
0231 },
0232 {
0233 .label = "host",
0234 .id = EXYNOS4210_HOST,
0235 .power_on = exynos4210_power_on,
0236 .power_off = exynos4210_power_off,
0237 },
0238 {
0239 .label = "hsic0",
0240 .id = EXYNOS4210_HSIC0,
0241 .power_on = exynos4210_power_on,
0242 .power_off = exynos4210_power_off,
0243 },
0244 {
0245 .label = "hsic1",
0246 .id = EXYNOS4210_HSIC1,
0247 .power_on = exynos4210_power_on,
0248 .power_off = exynos4210_power_off,
0249 },
0250 };
0251
0252 const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
0253 .has_mode_switch = 0,
0254 .num_phys = EXYNOS4210_NUM_PHYS,
0255 .phys = exynos4210_phys,
0256 .rate_to_clk = exynos4210_rate_to_clk,
0257 };