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 "phy-samsung-usb2.h"
0013
0014
0015
0016
0017 #define S5PV210_UPHYPWR 0x0
0018
0019 #define S5PV210_UPHYPWR_PHY0_SUSPEND BIT(0)
0020 #define S5PV210_UPHYPWR_PHY0_PWR BIT(3)
0021 #define S5PV210_UPHYPWR_PHY0_OTG_PWR BIT(4)
0022 #define S5PV210_UPHYPWR_PHY0 ( \
0023 S5PV210_UPHYPWR_PHY0_SUSPEND | \
0024 S5PV210_UPHYPWR_PHY0_PWR | \
0025 S5PV210_UPHYPWR_PHY0_OTG_PWR)
0026
0027 #define S5PV210_UPHYPWR_PHY1_SUSPEND BIT(6)
0028 #define S5PV210_UPHYPWR_PHY1_PWR BIT(7)
0029 #define S5PV210_UPHYPWR_PHY1 ( \
0030 S5PV210_UPHYPWR_PHY1_SUSPEND | \
0031 S5PV210_UPHYPWR_PHY1_PWR)
0032
0033
0034 #define S5PV210_UPHYCLK 0x4
0035
0036 #define S5PV210_UPHYCLK_PHYFSEL_MASK (0x3 << 0)
0037 #define S5PV210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0)
0038 #define S5PV210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0)
0039 #define S5PV210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0)
0040
0041 #define S5PV210_UPHYCLK_PHY0_ID_PULLUP BIT(2)
0042 #define S5PV210_UPHYCLK_PHY0_COMMON_ON BIT(4)
0043 #define S5PV210_UPHYCLK_PHY1_COMMON_ON BIT(7)
0044
0045
0046 #define S5PV210_UPHYRST 0x8
0047
0048 #define S5PV210_URSTCON_PHY0 BIT(0)
0049 #define S5PV210_URSTCON_OTG_HLINK BIT(1)
0050 #define S5PV210_URSTCON_OTG_PHYLINK BIT(2)
0051 #define S5PV210_URSTCON_PHY1_ALL BIT(3)
0052 #define S5PV210_URSTCON_HOST_LINK_ALL BIT(4)
0053
0054
0055 #define S5PV210_USB_ISOL_OFFSET 0x680c
0056 #define S5PV210_USB_ISOL_DEVICE BIT(0)
0057 #define S5PV210_USB_ISOL_HOST BIT(1)
0058
0059
0060 enum s5pv210_phy_id {
0061 S5PV210_DEVICE,
0062 S5PV210_HOST,
0063 S5PV210_NUM_PHYS,
0064 };
0065
0066
0067
0068
0069
0070 static int s5pv210_rate_to_clk(unsigned long rate, u32 *reg)
0071 {
0072 switch (rate) {
0073 case 12 * MHZ:
0074 *reg = S5PV210_UPHYCLK_PHYFSEL_12MHZ;
0075 break;
0076 case 24 * MHZ:
0077 *reg = S5PV210_UPHYCLK_PHYFSEL_24MHZ;
0078 break;
0079 case 48 * MHZ:
0080 *reg = S5PV210_UPHYCLK_PHYFSEL_48MHZ;
0081 break;
0082 default:
0083 return -EINVAL;
0084 }
0085
0086 return 0;
0087 }
0088
0089 static void s5pv210_isol(struct samsung_usb2_phy_instance *inst, bool on)
0090 {
0091 struct samsung_usb2_phy_driver *drv = inst->drv;
0092 u32 mask;
0093
0094 switch (inst->cfg->id) {
0095 case S5PV210_DEVICE:
0096 mask = S5PV210_USB_ISOL_DEVICE;
0097 break;
0098 case S5PV210_HOST:
0099 mask = S5PV210_USB_ISOL_HOST;
0100 break;
0101 default:
0102 return;
0103 }
0104
0105 regmap_update_bits(drv->reg_pmu, S5PV210_USB_ISOL_OFFSET,
0106 mask, on ? 0 : mask);
0107 }
0108
0109 static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
0110 {
0111 struct samsung_usb2_phy_driver *drv = inst->drv;
0112 u32 rstbits = 0;
0113 u32 phypwr = 0;
0114 u32 rst;
0115 u32 pwr;
0116
0117 switch (inst->cfg->id) {
0118 case S5PV210_DEVICE:
0119 phypwr = S5PV210_UPHYPWR_PHY0;
0120 rstbits = S5PV210_URSTCON_PHY0;
0121 break;
0122 case S5PV210_HOST:
0123 phypwr = S5PV210_UPHYPWR_PHY1;
0124 rstbits = S5PV210_URSTCON_PHY1_ALL |
0125 S5PV210_URSTCON_HOST_LINK_ALL;
0126 break;
0127 }
0128
0129 if (on) {
0130 writel(drv->ref_reg_val, drv->reg_phy + S5PV210_UPHYCLK);
0131
0132 pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
0133 pwr &= ~phypwr;
0134 writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
0135
0136 rst = readl(drv->reg_phy + S5PV210_UPHYRST);
0137 rst |= rstbits;
0138 writel(rst, drv->reg_phy + S5PV210_UPHYRST);
0139 udelay(10);
0140 rst &= ~rstbits;
0141 writel(rst, drv->reg_phy + S5PV210_UPHYRST);
0142
0143
0144
0145 udelay(80);
0146 } else {
0147 pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
0148 pwr |= phypwr;
0149 writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
0150 }
0151 }
0152
0153 static int s5pv210_power_on(struct samsung_usb2_phy_instance *inst)
0154 {
0155 s5pv210_isol(inst, 0);
0156 s5pv210_phy_pwr(inst, 1);
0157
0158 return 0;
0159 }
0160
0161 static int s5pv210_power_off(struct samsung_usb2_phy_instance *inst)
0162 {
0163 s5pv210_phy_pwr(inst, 0);
0164 s5pv210_isol(inst, 1);
0165
0166 return 0;
0167 }
0168
0169 static const struct samsung_usb2_common_phy s5pv210_phys[S5PV210_NUM_PHYS] = {
0170 [S5PV210_DEVICE] = {
0171 .label = "device",
0172 .id = S5PV210_DEVICE,
0173 .power_on = s5pv210_power_on,
0174 .power_off = s5pv210_power_off,
0175 },
0176 [S5PV210_HOST] = {
0177 .label = "host",
0178 .id = S5PV210_HOST,
0179 .power_on = s5pv210_power_on,
0180 .power_off = s5pv210_power_off,
0181 },
0182 };
0183
0184 const struct samsung_usb2_phy_config s5pv210_usb2_phy_config = {
0185 .num_phys = ARRAY_SIZE(s5pv210_phys),
0186 .phys = s5pv210_phys,
0187 .rate_to_clk = s5pv210_rate_to_clk,
0188 };