colibri_imx6: fix video stdout in default environment
[oweals/u-boot.git] / drivers / phy / phy-rcar-gen2.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Renesas RCar Gen2 USB PHY driver
4  *
5  * Copyright (C) 2018 Marek Vasut <marek.vasut@gmail.com>
6  */
7
8 #include <common.h>
9 #include <clk.h>
10 #include <div64.h>
11 #include <dm.h>
12 #include <fdtdec.h>
13 #include <generic-phy.h>
14 #include <malloc.h>
15 #include <reset.h>
16 #include <syscon.h>
17 #include <usb.h>
18 #include <asm/io.h>
19 #include <dm/device_compat.h>
20 #include <linux/bitops.h>
21 #include <linux/delay.h>
22 #include <power/regulator.h>
23
24 #define USBHS_LPSTS                     0x02
25 #define USBHS_UGCTRL                    0x80
26 #define USBHS_UGCTRL2                   0x84
27 #define USBHS_UGSTS                     0x88    /* From technical update */
28
29 /* Low Power Status register (LPSTS) */
30 #define USBHS_LPSTS_SUSPM               0x4000
31
32 /* USB General control register (UGCTRL) */
33 #define USBHS_UGCTRL_CONNECT            BIT(2)
34 #define USBHS_UGCTRL_PLLRESET           BIT(0)
35
36 /* USB General control register 2 (UGCTRL2) */
37 #define USBHS_UGCTRL2_USB2SEL           0x80000000
38 #define USBHS_UGCTRL2_USB2SEL_PCI       0x00000000
39 #define USBHS_UGCTRL2_USB2SEL_USB30     0x80000000
40 #define USBHS_UGCTRL2_USB0SEL           0x00000030
41 #define USBHS_UGCTRL2_USB0SEL_PCI       0x00000010
42 #define USBHS_UGCTRL2_USB0SEL_HS_USB    0x00000030
43
44 /* USB General status register (UGSTS) */
45 #define USBHS_UGSTS_LOCK                0x00000100 /* From technical update */
46
47 #define PHYS_PER_CHANNEL        2
48
49 struct rcar_gen2_phy {
50         fdt_addr_t      regs;
51         struct clk      clk;
52 };
53
54 static int rcar_gen2_phy_phy_init(struct phy *phy)
55 {
56         struct rcar_gen2_phy *priv = dev_get_priv(phy->dev);
57         u16 chan = phy->id & 0xffff;
58         u16 mode = (phy->id >> 16) & 0xffff;
59         u32 clrmask, setmask;
60
61         if (chan == 0) {
62                 clrmask = USBHS_UGCTRL2_USB0SEL;
63                 setmask = mode ? USBHS_UGCTRL2_USB0SEL_HS_USB :
64                                  USBHS_UGCTRL2_USB0SEL_PCI;
65         } else {
66                 clrmask = USBHS_UGCTRL2_USB2SEL;
67                 setmask = mode ? USBHS_UGCTRL2_USB2SEL_USB30 :
68                                  USBHS_UGCTRL2_USB2SEL_PCI;
69         }
70         clrsetbits_le32(priv->regs + USBHS_UGCTRL2, clrmask, setmask);
71
72         return 0;
73 }
74
75 static int rcar_gen2_phy_phy_power_on(struct phy *phy)
76 {
77         struct rcar_gen2_phy *priv = dev_get_priv(phy->dev);
78         int i;
79         u32 value;
80
81         /* Power on USBHS PHY */
82         clrbits_le32(priv->regs + USBHS_UGCTRL, USBHS_UGCTRL_PLLRESET);
83
84         setbits_le16(priv->regs + USBHS_LPSTS, USBHS_LPSTS_SUSPM);
85
86         for (i = 0; i < 20; i++) {
87                 value = readl(priv->regs + USBHS_UGSTS);
88                 if ((value & USBHS_UGSTS_LOCK) == USBHS_UGSTS_LOCK) {
89                         setbits_le32(priv->regs + USBHS_UGCTRL,
90                                      USBHS_UGCTRL_CONNECT);
91                         return 0;
92                 }
93                 udelay(1);
94         }
95
96         return -ETIMEDOUT;
97 }
98
99 static int rcar_gen2_phy_phy_power_off(struct phy *phy)
100 {
101         struct rcar_gen2_phy *priv = dev_get_priv(phy->dev);
102
103         /* Power off USBHS PHY */
104         clrbits_le32(priv->regs + USBHS_UGCTRL, USBHS_UGCTRL_CONNECT);
105
106         clrbits_le16(priv->regs + USBHS_LPSTS, USBHS_LPSTS_SUSPM);
107
108         setbits_le32(priv->regs + USBHS_UGCTRL, USBHS_UGCTRL_PLLRESET);
109
110         return 0;
111 }
112
113 static int rcar_gen2_phy_of_xlate(struct phy *phy,
114                                   struct ofnode_phandle_args *args)
115 {
116         if (args->args_count != 2) {
117                 dev_err(phy->dev, "Invalid DT PHY argument count: %d\n",
118                         args->args_count);
119                 return -EINVAL;
120         }
121
122         if (args->args[0] != 0 && args->args[0] != 2) {
123                 dev_err(phy->dev, "Invalid DT PHY channel: %d\n",
124                         args->args[0]);
125                 return -EINVAL;
126         }
127
128         if (args->args[1] != 0 && args->args[1] != 1) {
129                 dev_err(phy->dev, "Invalid DT PHY mode: %d\n",
130                         args->args[1]);
131                 return -EINVAL;
132         }
133
134         if (args->args_count)
135                 phy->id = args->args[0] | (args->args[1] << 16);
136         else
137                 phy->id = 0;
138
139         return 0;
140 }
141
142 static const struct phy_ops rcar_gen2_phy_phy_ops = {
143         .init           = rcar_gen2_phy_phy_init,
144         .power_on       = rcar_gen2_phy_phy_power_on,
145         .power_off      = rcar_gen2_phy_phy_power_off,
146         .of_xlate       = rcar_gen2_phy_of_xlate,
147 };
148
149 static int rcar_gen2_phy_probe(struct udevice *dev)
150 {
151         struct rcar_gen2_phy *priv = dev_get_priv(dev);
152         int ret;
153
154         priv->regs = dev_read_addr(dev);
155         if (priv->regs == FDT_ADDR_T_NONE)
156                 return -EINVAL;
157
158         /* Enable clock */
159         ret = clk_get_by_index(dev, 0, &priv->clk);
160         if (ret)
161                 return ret;
162
163         ret = clk_enable(&priv->clk);
164         if (ret)
165                 return ret;
166
167         return 0;
168 }
169
170 static int rcar_gen2_phy_remove(struct udevice *dev)
171 {
172         struct rcar_gen2_phy *priv = dev_get_priv(dev);
173
174         clk_disable(&priv->clk);
175         clk_free(&priv->clk);
176
177         return 0;
178 }
179
180 static const struct udevice_id rcar_gen2_phy_of_match[] = {
181         { .compatible = "renesas,rcar-gen2-usb-phy", },
182         { },
183 };
184
185 U_BOOT_DRIVER(rcar_gen2_phy) = {
186         .name           = "rcar-gen2-phy",
187         .id             = UCLASS_PHY,
188         .of_match       = rcar_gen2_phy_of_match,
189         .ops            = &rcar_gen2_phy_phy_ops,
190         .probe          = rcar_gen2_phy_probe,
191         .remove         = rcar_gen2_phy_remove,
192         .priv_auto_alloc_size = sizeof(struct rcar_gen2_phy),
193 };