]>
Commit | Line | Data |
---|---|---|
898e7610 MV |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (C) 2022 Marek Vasut <marex@denx.de> | |
4 | */ | |
5 | ||
d678a59d | 6 | #include <common.h> |
898e7610 MV |
7 | #include <asm/io.h> |
8 | #include <clk.h> | |
0d588cb7 | 9 | #include <clk-uclass.h> |
898e7610 MV |
10 | #include <dm.h> |
11 | #include <dm/device.h> | |
12 | #include <dm/device_compat.h> | |
0d588cb7 SG |
13 | #include <dm/device-internal.h> |
14 | #include <dm/lists.h> | |
15 | #include <linux/bitfield.h> | |
16 | #include <linux/delay.h> | |
17 | #include <linux/iopoll.h> | |
898e7610 MV |
18 | #include <power-domain-uclass.h> |
19 | ||
20 | #include <dt-bindings/power/imx8mp-power.h> | |
21 | ||
22 | #define GPR_REG0 0x0 | |
23 | #define PCIE_CLOCK_MODULE_EN BIT(0) | |
24 | #define USB_CLOCK_MODULE_EN BIT(1) | |
c7ab1442 SG |
25 | #define PCIE_PHY_APB_RST BIT(4) |
26 | #define PCIE_PHY_INIT_RST BIT(5) | |
0d588cb7 SG |
27 | #define GPR_REG1 0x4 |
28 | #define PLL_LOCK BIT(13) | |
29 | #define GPR_REG2 0x8 | |
30 | #define P_PLL_MASK GENMASK(5, 0) | |
31 | #define M_PLL_MASK GENMASK(15, 6) | |
32 | #define S_PLL_MASK GENMASK(18, 16) | |
33 | #define GPR_REG3 0xc | |
34 | #define PLL_CKE BIT(17) | |
35 | #define PLL_RST BIT(31) | |
898e7610 MV |
36 | |
37 | struct imx8mp_hsiomix_priv { | |
38 | void __iomem *base; | |
39 | struct clk clk_usb; | |
c7ab1442 | 40 | struct clk clk_pcie; |
898e7610 MV |
41 | struct power_domain pd_bus; |
42 | struct power_domain pd_usb; | |
c7ab1442 | 43 | struct power_domain pd_pcie; |
898e7610 MV |
44 | struct power_domain pd_usb_phy1; |
45 | struct power_domain pd_usb_phy2; | |
c7ab1442 | 46 | struct power_domain pd_pcie_phy; |
898e7610 MV |
47 | }; |
48 | ||
c7ab1442 | 49 | static int imx8mp_hsiomix_set(struct power_domain *power_domain, bool power_on) |
898e7610 MV |
50 | { |
51 | struct udevice *dev = power_domain->dev; | |
52 | struct imx8mp_hsiomix_priv *priv = dev_get_priv(dev); | |
c7ab1442 SG |
53 | struct power_domain *domain = NULL; |
54 | struct clk *clk = NULL; | |
55 | u32 gpr_reg0_bits = 0; | |
898e7610 MV |
56 | int ret; |
57 | ||
c7ab1442 SG |
58 | switch (power_domain->id) { |
59 | case IMX8MP_HSIOBLK_PD_USB: | |
898e7610 | 60 | domain = &priv->pd_usb; |
c7ab1442 SG |
61 | clk = &priv->clk_usb; |
62 | gpr_reg0_bits |= USB_CLOCK_MODULE_EN; | |
63 | break; | |
64 | case IMX8MP_HSIOBLK_PD_USB_PHY1: | |
898e7610 | 65 | domain = &priv->pd_usb_phy1; |
c7ab1442 SG |
66 | break; |
67 | case IMX8MP_HSIOBLK_PD_USB_PHY2: | |
898e7610 | 68 | domain = &priv->pd_usb_phy2; |
c7ab1442 SG |
69 | break; |
70 | case IMX8MP_HSIOBLK_PD_PCIE: | |
71 | domain = &priv->pd_pcie; | |
72 | clk = &priv->clk_pcie; | |
73 | gpr_reg0_bits |= PCIE_CLOCK_MODULE_EN; | |
74 | break; | |
75 | case IMX8MP_HSIOBLK_PD_PCIE_PHY: | |
76 | domain = &priv->pd_pcie_phy; | |
77 | /* Bits to deassert PCIe PHY reset */ | |
78 | gpr_reg0_bits |= PCIE_PHY_APB_RST | PCIE_PHY_INIT_RST; | |
79 | break; | |
80 | default: | |
81 | dev_err(dev, "unknown power domain id: %ld\n", | |
82 | power_domain->id); | |
83 | return -EINVAL; | |
898e7610 MV |
84 | } |
85 | ||
c7ab1442 SG |
86 | if (power_on) { |
87 | ret = power_domain_on(&priv->pd_bus); | |
88 | if (ret) | |
89 | return ret; | |
898e7610 | 90 | |
c7ab1442 SG |
91 | ret = power_domain_on(domain); |
92 | if (ret) | |
93 | goto err_pd; | |
898e7610 | 94 | |
c7ab1442 SG |
95 | if (clk) { |
96 | ret = clk_enable(clk); | |
97 | if (ret) | |
98 | goto err_clk; | |
99 | } | |
100 | ||
101 | if (gpr_reg0_bits) | |
102 | setbits_le32(priv->base + GPR_REG0, gpr_reg0_bits); | |
103 | } else { | |
104 | if (gpr_reg0_bits) | |
105 | clrbits_le32(priv->base + GPR_REG0, gpr_reg0_bits); | |
106 | ||
107 | if (clk) | |
108 | clk_disable(clk); | |
109 | ||
110 | power_domain_off(domain); | |
111 | power_domain_off(&priv->pd_bus); | |
112 | } | |
898e7610 MV |
113 | |
114 | return 0; | |
115 | ||
116 | err_clk: | |
117 | power_domain_off(domain); | |
118 | err_pd: | |
119 | power_domain_off(&priv->pd_bus); | |
120 | return ret; | |
121 | } | |
122 | ||
c7ab1442 | 123 | static int imx8mp_hsiomix_on(struct power_domain *power_domain) |
898e7610 | 124 | { |
c7ab1442 SG |
125 | return imx8mp_hsiomix_set(power_domain, true); |
126 | } | |
898e7610 | 127 | |
c7ab1442 SG |
128 | static int imx8mp_hsiomix_off(struct power_domain *power_domain) |
129 | { | |
130 | return imx8mp_hsiomix_set(power_domain, false); | |
898e7610 MV |
131 | } |
132 | ||
133 | static int imx8mp_hsiomix_of_xlate(struct power_domain *power_domain, | |
134 | struct ofnode_phandle_args *args) | |
135 | { | |
136 | power_domain->id = args->args[0]; | |
137 | ||
138 | return 0; | |
139 | } | |
140 | ||
0d588cb7 SG |
141 | static int hsio_pll_clk_enable(struct clk *clk) |
142 | { | |
143 | void *base = (void *)dev_get_driver_data(clk->dev); | |
144 | u32 val; | |
145 | int ret; | |
146 | ||
147 | /* Setup HSIO PLL as 100 MHz output clock */ | |
148 | clrsetbits_le32(base + GPR_REG2, | |
149 | P_PLL_MASK | M_PLL_MASK | S_PLL_MASK, | |
150 | FIELD_PREP(P_PLL_MASK, 12) | | |
151 | FIELD_PREP(M_PLL_MASK, 800) | | |
152 | FIELD_PREP(S_PLL_MASK, 4)); | |
153 | ||
154 | /* de-assert PLL reset */ | |
155 | setbits_le32(base + GPR_REG3, PLL_RST); | |
156 | ||
157 | /* enable PLL */ | |
158 | setbits_le32(base + GPR_REG3, PLL_CKE); | |
159 | ||
160 | /* Check if PLL is locked */ | |
161 | ret = readl_poll_sleep_timeout(base + GPR_REG1, val, | |
162 | val & PLL_LOCK, 10, 100000); | |
163 | if (ret) | |
164 | dev_err(clk->dev, "failed to lock HSIO PLL\n"); | |
165 | ||
166 | return ret; | |
167 | } | |
168 | ||
169 | static int hsio_pll_clk_disable(struct clk *clk) | |
170 | { | |
171 | void *base = (void *)dev_get_driver_data(clk->dev); | |
172 | ||
173 | clrbits_le32(base + GPR_REG3, PLL_CKE | PLL_RST); | |
174 | ||
175 | return 0; | |
176 | } | |
177 | ||
178 | static const struct clk_ops hsio_pll_clk_ops = { | |
179 | .enable = hsio_pll_clk_enable, | |
180 | .disable = hsio_pll_clk_disable, | |
181 | }; | |
182 | ||
183 | U_BOOT_DRIVER(hsio_pll) = { | |
184 | .name = "hsio-pll", | |
185 | .id = UCLASS_CLK, | |
186 | .ops = &hsio_pll_clk_ops, | |
187 | }; | |
188 | ||
189 | int imx8mp_hsiomix_bind(struct udevice *dev) | |
190 | { | |
191 | struct driver *drv; | |
192 | ||
193 | drv = lists_driver_lookup_name("hsio-pll"); | |
194 | if (!drv) | |
195 | return -ENOENT; | |
196 | ||
197 | return device_bind_with_driver_data(dev, drv, "hsio-pll", | |
198 | (ulong)dev_read_addr_ptr(dev), | |
199 | dev_ofnode(dev), NULL); | |
200 | } | |
201 | ||
898e7610 MV |
202 | static int imx8mp_hsiomix_probe(struct udevice *dev) |
203 | { | |
204 | struct imx8mp_hsiomix_priv *priv = dev_get_priv(dev); | |
205 | int ret; | |
206 | ||
207 | priv->base = dev_read_addr_ptr(dev); | |
208 | ||
209 | ret = clk_get_by_name(dev, "usb", &priv->clk_usb); | |
210 | if (ret < 0) | |
211 | return ret; | |
212 | ||
c7ab1442 SG |
213 | ret = clk_get_by_name(dev, "pcie", &priv->clk_pcie); |
214 | if (ret < 0) | |
215 | return ret; | |
216 | ||
898e7610 MV |
217 | ret = power_domain_get_by_name(dev, &priv->pd_bus, "bus"); |
218 | if (ret < 0) | |
c9309f40 | 219 | return ret; |
898e7610 MV |
220 | |
221 | ret = power_domain_get_by_name(dev, &priv->pd_usb, "usb"); | |
222 | if (ret < 0) | |
223 | goto err_pd_usb; | |
224 | ||
225 | ret = power_domain_get_by_name(dev, &priv->pd_usb_phy1, "usb-phy1"); | |
226 | if (ret < 0) | |
227 | goto err_pd_usb_phy1; | |
228 | ||
229 | ret = power_domain_get_by_name(dev, &priv->pd_usb_phy2, "usb-phy2"); | |
230 | if (ret < 0) | |
231 | goto err_pd_usb_phy2; | |
232 | ||
c7ab1442 SG |
233 | ret = power_domain_get_by_name(dev, &priv->pd_pcie, "pcie"); |
234 | if (ret < 0) | |
235 | goto err_pd_pcie; | |
236 | ||
237 | ret = power_domain_get_by_name(dev, &priv->pd_pcie_phy, "pcie-phy"); | |
238 | if (ret < 0) | |
239 | goto err_pd_pcie_phy; | |
240 | ||
898e7610 MV |
241 | return 0; |
242 | ||
c7ab1442 SG |
243 | err_pd_pcie_phy: |
244 | power_domain_free(&priv->pd_pcie); | |
245 | err_pd_pcie: | |
246 | power_domain_free(&priv->pd_usb_phy2); | |
898e7610 MV |
247 | err_pd_usb_phy2: |
248 | power_domain_free(&priv->pd_usb_phy1); | |
249 | err_pd_usb_phy1: | |
250 | power_domain_free(&priv->pd_usb); | |
251 | err_pd_usb: | |
252 | power_domain_free(&priv->pd_bus); | |
898e7610 MV |
253 | return ret; |
254 | } | |
255 | ||
256 | static const struct udevice_id imx8mp_hsiomix_ids[] = { | |
257 | { .compatible = "fsl,imx8mp-hsio-blk-ctrl" }, | |
258 | { } | |
259 | }; | |
260 | ||
261 | struct power_domain_ops imx8mp_hsiomix_ops = { | |
262 | .on = imx8mp_hsiomix_on, | |
263 | .off = imx8mp_hsiomix_off, | |
264 | .of_xlate = imx8mp_hsiomix_of_xlate, | |
265 | }; | |
266 | ||
267 | U_BOOT_DRIVER(imx8mp_hsiomix) = { | |
268 | .name = "imx8mp_hsiomix", | |
269 | .id = UCLASS_POWER_DOMAIN, | |
270 | .of_match = imx8mp_hsiomix_ids, | |
271 | .probe = imx8mp_hsiomix_probe, | |
0d588cb7 | 272 | .bind = imx8mp_hsiomix_bind, |
898e7610 MV |
273 | .priv_auto = sizeof(struct imx8mp_hsiomix_priv), |
274 | .ops = &imx8mp_hsiomix_ops, | |
275 | }; |