Merge tag 'asoc-fix-v5.14-rc2' of https://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / drivers / phy / samsung / phy-s5pv210-usb2.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Samsung SoC USB 1.1/2.0 PHY driver - S5PV210 support
4  *
5  * Copyright (C) 2013 Samsung Electronics Co., Ltd.
6  * Authors: Kamil Debski <k.debski@samsung.com>
7  */
8
9 #include <linux/delay.h>
10 #include <linux/io.h>
11 #include <linux/phy/phy.h>
12 #include "phy-samsung-usb2.h"
13
14 /* Exynos USB PHY registers */
15
16 /* PHY power control */
17 #define S5PV210_UPHYPWR                 0x0
18
19 #define S5PV210_UPHYPWR_PHY0_SUSPEND    BIT(0)
20 #define S5PV210_UPHYPWR_PHY0_PWR        BIT(3)
21 #define S5PV210_UPHYPWR_PHY0_OTG_PWR    BIT(4)
22 #define S5PV210_UPHYPWR_PHY0    ( \
23         S5PV210_UPHYPWR_PHY0_SUSPEND | \
24         S5PV210_UPHYPWR_PHY0_PWR | \
25         S5PV210_UPHYPWR_PHY0_OTG_PWR)
26
27 #define S5PV210_UPHYPWR_PHY1_SUSPEND    BIT(6)
28 #define S5PV210_UPHYPWR_PHY1_PWR        BIT(7)
29 #define S5PV210_UPHYPWR_PHY1 ( \
30         S5PV210_UPHYPWR_PHY1_SUSPEND | \
31         S5PV210_UPHYPWR_PHY1_PWR)
32
33 /* PHY clock control */
34 #define S5PV210_UPHYCLK                 0x4
35
36 #define S5PV210_UPHYCLK_PHYFSEL_MASK    (0x3 << 0)
37 #define S5PV210_UPHYCLK_PHYFSEL_48MHZ   (0x0 << 0)
38 #define S5PV210_UPHYCLK_PHYFSEL_24MHZ   (0x3 << 0)
39 #define S5PV210_UPHYCLK_PHYFSEL_12MHZ   (0x2 << 0)
40
41 #define S5PV210_UPHYCLK_PHY0_ID_PULLUP  BIT(2)
42 #define S5PV210_UPHYCLK_PHY0_COMMON_ON  BIT(4)
43 #define S5PV210_UPHYCLK_PHY1_COMMON_ON  BIT(7)
44
45 /* PHY reset control */
46 #define S5PV210_UPHYRST                 0x8
47
48 #define S5PV210_URSTCON_PHY0            BIT(0)
49 #define S5PV210_URSTCON_OTG_HLINK       BIT(1)
50 #define S5PV210_URSTCON_OTG_PHYLINK     BIT(2)
51 #define S5PV210_URSTCON_PHY1_ALL        BIT(3)
52 #define S5PV210_URSTCON_HOST_LINK_ALL   BIT(4)
53
54 /* Isolation, configured in the power management unit */
55 #define S5PV210_USB_ISOL_OFFSET         0x680c
56 #define S5PV210_USB_ISOL_DEVICE         BIT(0)
57 #define S5PV210_USB_ISOL_HOST           BIT(1)
58
59
60 enum s5pv210_phy_id {
61         S5PV210_DEVICE,
62         S5PV210_HOST,
63         S5PV210_NUM_PHYS,
64 };
65
66 /*
67  * s5pv210_rate_to_clk() converts the supplied clock rate to the value that
68  * can be written to the phy register.
69  */
70 static int s5pv210_rate_to_clk(unsigned long rate, u32 *reg)
71 {
72         switch (rate) {
73         case 12 * MHZ:
74                 *reg = S5PV210_UPHYCLK_PHYFSEL_12MHZ;
75                 break;
76         case 24 * MHZ:
77                 *reg = S5PV210_UPHYCLK_PHYFSEL_24MHZ;
78                 break;
79         case 48 * MHZ:
80                 *reg = S5PV210_UPHYCLK_PHYFSEL_48MHZ;
81                 break;
82         default:
83                 return -EINVAL;
84         }
85
86         return 0;
87 }
88
89 static void s5pv210_isol(struct samsung_usb2_phy_instance *inst, bool on)
90 {
91         struct samsung_usb2_phy_driver *drv = inst->drv;
92         u32 mask;
93
94         switch (inst->cfg->id) {
95         case S5PV210_DEVICE:
96                 mask = S5PV210_USB_ISOL_DEVICE;
97                 break;
98         case S5PV210_HOST:
99                 mask = S5PV210_USB_ISOL_HOST;
100                 break;
101         default:
102                 return;
103         }
104
105         regmap_update_bits(drv->reg_pmu, S5PV210_USB_ISOL_OFFSET,
106                                                         mask, on ? 0 : mask);
107 }
108
109 static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
110 {
111         struct samsung_usb2_phy_driver *drv = inst->drv;
112         u32 rstbits = 0;
113         u32 phypwr = 0;
114         u32 rst;
115         u32 pwr;
116
117         switch (inst->cfg->id) {
118         case S5PV210_DEVICE:
119                 phypwr =        S5PV210_UPHYPWR_PHY0;
120                 rstbits =       S5PV210_URSTCON_PHY0;
121                 break;
122         case S5PV210_HOST:
123                 phypwr =        S5PV210_UPHYPWR_PHY1;
124                 rstbits =       S5PV210_URSTCON_PHY1_ALL |
125                                 S5PV210_URSTCON_HOST_LINK_ALL;
126                 break;
127         }
128
129         if (on) {
130                 writel(drv->ref_reg_val, drv->reg_phy + S5PV210_UPHYCLK);
131
132                 pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
133                 pwr &= ~phypwr;
134                 writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
135
136                 rst = readl(drv->reg_phy + S5PV210_UPHYRST);
137                 rst |= rstbits;
138                 writel(rst, drv->reg_phy + S5PV210_UPHYRST);
139                 udelay(10);
140                 rst &= ~rstbits;
141                 writel(rst, drv->reg_phy + S5PV210_UPHYRST);
142                 /* The following delay is necessary for the reset sequence to be
143                  * completed
144                  */
145                 udelay(80);
146         } else {
147                 pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
148                 pwr |= phypwr;
149                 writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
150         }
151 }
152
153 static int s5pv210_power_on(struct samsung_usb2_phy_instance *inst)
154 {
155         s5pv210_isol(inst, 0);
156         s5pv210_phy_pwr(inst, 1);
157
158         return 0;
159 }
160
161 static int s5pv210_power_off(struct samsung_usb2_phy_instance *inst)
162 {
163         s5pv210_phy_pwr(inst, 0);
164         s5pv210_isol(inst, 1);
165
166         return 0;
167 }
168
169 static const struct samsung_usb2_common_phy s5pv210_phys[S5PV210_NUM_PHYS] = {
170         [S5PV210_DEVICE] = {
171                 .label          = "device",
172                 .id             = S5PV210_DEVICE,
173                 .power_on       = s5pv210_power_on,
174                 .power_off      = s5pv210_power_off,
175         },
176         [S5PV210_HOST] = {
177                 .label          = "host",
178                 .id             = S5PV210_HOST,
179                 .power_on       = s5pv210_power_on,
180                 .power_off      = s5pv210_power_off,
181         },
182 };
183
184 const struct samsung_usb2_phy_config s5pv210_usb2_phy_config = {
185         .num_phys       = ARRAY_SIZE(s5pv210_phys),
186         .phys           = s5pv210_phys,
187         .rate_to_clk    = s5pv210_rate_to_clk,
188 };