]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/phy/samsung/phy-s5pv210-usb2.c
Merge tag 'selinux-pr-20191007' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux.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         } else {
143                 pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
144                 pwr |= phypwr;
145                 writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
146         }
147 }
148
149 static int s5pv210_power_on(struct samsung_usb2_phy_instance *inst)
150 {
151         s5pv210_isol(inst, 0);
152         s5pv210_phy_pwr(inst, 1);
153
154         return 0;
155 }
156
157 static int s5pv210_power_off(struct samsung_usb2_phy_instance *inst)
158 {
159         s5pv210_phy_pwr(inst, 0);
160         s5pv210_isol(inst, 1);
161
162         return 0;
163 }
164
165 static const struct samsung_usb2_common_phy s5pv210_phys[S5PV210_NUM_PHYS] = {
166         [S5PV210_DEVICE] = {
167                 .label          = "device",
168                 .id             = S5PV210_DEVICE,
169                 .power_on       = s5pv210_power_on,
170                 .power_off      = s5pv210_power_off,
171         },
172         [S5PV210_HOST] = {
173                 .label          = "host",
174                 .id             = S5PV210_HOST,
175                 .power_on       = s5pv210_power_on,
176                 .power_off      = s5pv210_power_off,
177         },
178 };
179
180 const struct samsung_usb2_phy_config s5pv210_usb2_phy_config = {
181         .num_phys       = ARRAY_SIZE(s5pv210_phys),
182         .phys           = s5pv210_phys,
183         .rate_to_clk    = s5pv210_rate_to_clk,
184 };