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