1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2022 Marek Vasut <marex@denx.de>
4 */
5
6#include <common.h>
7#include <asm/io.h>
8#include <clk.h>
9#include <clk-uclass.h>
10#include <dm.h>
11#include <dm/device.h>
12#include <dm/device_compat.h>
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>
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)
25#define  PCIE_PHY_APB_RST	BIT(4)
26#define  PCIE_PHY_INIT_RST	BIT(5)
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)
36
37struct imx8mp_hsiomix_priv {
38	void __iomem *base;
39	struct clk clk_usb;
40	struct clk clk_pcie;
41	struct power_domain pd_bus;
42	struct power_domain pd_usb;
43	struct power_domain pd_pcie;
44	struct power_domain pd_usb_phy1;
45	struct power_domain pd_usb_phy2;
46	struct power_domain pd_pcie_phy;
47};
48
49static int imx8mp_hsiomix_set(struct power_domain *power_domain, bool power_on)
50{
51	struct udevice *dev = power_domain->dev;
52	struct imx8mp_hsiomix_priv *priv = dev_get_priv(dev);
53	struct power_domain *domain = NULL;
54	struct clk *clk = NULL;
55	u32 gpr_reg0_bits = 0;
56	int ret;
57
58	switch (power_domain->id) {
59	case IMX8MP_HSIOBLK_PD_USB:
60		domain = &priv->pd_usb;
61		clk = &priv->clk_usb;
62		gpr_reg0_bits |= USB_CLOCK_MODULE_EN;
63		break;
64	case IMX8MP_HSIOBLK_PD_USB_PHY1:
65		domain = &priv->pd_usb_phy1;
66		break;
67	case IMX8MP_HSIOBLK_PD_USB_PHY2:
68		domain = &priv->pd_usb_phy2;
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;
84	}
85
86	if (power_on) {
87		ret = power_domain_on(&priv->pd_bus);
88		if (ret)
89			return ret;
90
91		ret = power_domain_on(domain);
92		if (ret)
93			goto err_pd;
94
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	}
113
114	return 0;
115
116err_clk:
117	power_domain_off(domain);
118err_pd:
119	power_domain_off(&priv->pd_bus);
120	return ret;
121}
122
123static int imx8mp_hsiomix_on(struct power_domain *power_domain)
124{
125	return imx8mp_hsiomix_set(power_domain, true);
126}
127
128static int imx8mp_hsiomix_off(struct power_domain *power_domain)
129{
130	return imx8mp_hsiomix_set(power_domain, false);
131}
132
133static 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
141static 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
169static 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
178static const struct clk_ops hsio_pll_clk_ops = {
179	.enable = hsio_pll_clk_enable,
180	.disable = hsio_pll_clk_disable,
181};
182
183U_BOOT_DRIVER(hsio_pll) = {
184	.name = "hsio-pll",
185	.id = UCLASS_CLK,
186	.ops = &hsio_pll_clk_ops,
187};
188
189int 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
202static 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
213	ret = clk_get_by_name(dev, "pcie", &priv->clk_pcie);
214	if (ret < 0)
215		return ret;
216
217	ret = power_domain_get_by_name(dev, &priv->pd_bus, "bus");
218	if (ret < 0)
219		return ret;
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
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
241	return 0;
242
243err_pd_pcie_phy:
244	power_domain_free(&priv->pd_pcie);
245err_pd_pcie:
246	power_domain_free(&priv->pd_usb_phy2);
247err_pd_usb_phy2:
248	power_domain_free(&priv->pd_usb_phy1);
249err_pd_usb_phy1:
250	power_domain_free(&priv->pd_usb);
251err_pd_usb:
252	power_domain_free(&priv->pd_bus);
253	return ret;
254}
255
256static const struct udevice_id imx8mp_hsiomix_ids[] = {
257	{ .compatible = "fsl,imx8mp-hsio-blk-ctrl" },
258	{ }
259};
260
261struct 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
267U_BOOT_DRIVER(imx8mp_hsiomix) = {
268	.name		= "imx8mp_hsiomix",
269	.id		= UCLASS_POWER_DOMAIN,
270	.of_match	= imx8mp_hsiomix_ids,
271	.probe		= imx8mp_hsiomix_probe,
272	.bind		= imx8mp_hsiomix_bind,
273	.priv_auto	= sizeof(struct imx8mp_hsiomix_priv),
274	.ops		= &imx8mp_hsiomix_ops,
275};
276