1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2018 MediaTek Inc.
4 * Author: Ryder Lee <ryder.lee@mediatek.com>
5 */
6
7#include <clk.h>
8#include <common.h>
9#include <dm.h>
10#include <malloc.h>
11#include <power-domain-uclass.h>
12#include <regmap.h>
13#include <syscon.h>
14#include <asm/io.h>
15#include <asm/processor.h>
16#include <linux/bitops.h>
17#include <linux/err.h>
18#include <linux/iopoll.h>
19
20#include <dt-bindings/power/mt7623-power.h>
21#include <dt-bindings/power/mt7629-power.h>
22
23#define SPM_EN			(0xb16 << 16 | 0x1)
24#define SPM_VDE_PWR_CON		0x0210
25#define SPM_MFG_PWR_CON		0x0214
26#define SPM_ISP_PWR_CON		0x0238
27#define SPM_DIS_PWR_CON		0x023c
28#define SPM_CONN_PWR_CON	0x0280
29#define SPM_BDP_PWR_CON		0x029c
30#define SPM_ETH_PWR_CON		0x02a0
31#define SPM_HIF_PWR_CON		0x02a4
32#define SPM_IFR_MSC_PWR_CON	0x02a8
33#define SPM_ETHSYS_PWR_CON	0x2e0
34#define SPM_HIF0_PWR_CON	0x2e4
35#define SPM_HIF1_PWR_CON	0x2e8
36#define SPM_PWR_STATUS		0x60c
37#define SPM_PWR_STATUS_2ND	0x610
38
39#define PWR_RST_B_BIT		BIT(0)
40#define PWR_ISO_BIT		BIT(1)
41#define PWR_ON_BIT		BIT(2)
42#define PWR_ON_2ND_BIT		BIT(3)
43#define PWR_CLK_DIS_BIT		BIT(4)
44
45#define PWR_STATUS_CONN		BIT(1)
46#define PWR_STATUS_DISP		BIT(3)
47#define PWR_STATUS_MFG		BIT(4)
48#define PWR_STATUS_ISP		BIT(5)
49#define PWR_STATUS_VDEC		BIT(7)
50#define PWR_STATUS_BDP		BIT(14)
51#define PWR_STATUS_ETH		BIT(15)
52#define PWR_STATUS_HIF		BIT(16)
53#define PWR_STATUS_IFR_MSC	BIT(17)
54#define PWR_STATUS_ETHSYS	BIT(24)
55#define PWR_STATUS_HIF0		BIT(25)
56#define PWR_STATUS_HIF1		BIT(26)
57
58/* Infrasys configuration */
59#define INFRA_TOPDCM_CTRL	0x10
60#define INFRA_TOPAXI_PROT_EN	0x220
61#define INFRA_TOPAXI_PROT_STA1	0x228
62
63#define DCM_TOP_EN		BIT(0)
64
65enum scp_domain_type {
66	SCPSYS_MT7622,
67	SCPSYS_MT7623,
68	SCPSYS_MT7629,
69};
70
71struct scp_domain;
72
73struct scp_domain_data {
74	struct scp_domain *scpd;
75	u32 sta_mask;
76	int ctl_offs;
77	u32 sram_pdn_bits;
78	u32 sram_pdn_ack_bits;
79	u32 bus_prot_mask;
80};
81
82struct scp_domain {
83	void __iomem *base;
84	void __iomem *infracfg;
85	enum scp_domain_type type;
86	struct scp_domain_data *data;
87};
88
89static struct scp_domain_data scp_domain_mt7623[] = {
90	[MT7623_POWER_DOMAIN_CONN] = {
91		.sta_mask = PWR_STATUS_CONN,
92		.ctl_offs = SPM_CONN_PWR_CON,
93		.bus_prot_mask = BIT(8) | BIT(2),
94	},
95	[MT7623_POWER_DOMAIN_DISP] = {
96		.sta_mask = PWR_STATUS_DISP,
97		.ctl_offs = SPM_DIS_PWR_CON,
98		.sram_pdn_bits = GENMASK(11, 8),
99		.bus_prot_mask = BIT(2),
100	},
101	[MT7623_POWER_DOMAIN_MFG] = {
102		.sta_mask = PWR_STATUS_MFG,
103		.ctl_offs = SPM_MFG_PWR_CON,
104		.sram_pdn_bits = GENMASK(11, 8),
105		.sram_pdn_ack_bits = GENMASK(12, 12),
106	},
107	[MT7623_POWER_DOMAIN_VDEC] = {
108		.sta_mask = PWR_STATUS_VDEC,
109		.ctl_offs = SPM_VDE_PWR_CON,
110		.sram_pdn_bits = GENMASK(11, 8),
111		.sram_pdn_ack_bits = GENMASK(12, 12),
112	},
113	[MT7623_POWER_DOMAIN_ISP] = {
114		.sta_mask = PWR_STATUS_ISP,
115		.ctl_offs = SPM_ISP_PWR_CON,
116		.sram_pdn_bits = GENMASK(11, 8),
117		.sram_pdn_ack_bits = GENMASK(13, 12),
118	},
119	[MT7623_POWER_DOMAIN_BDP] = {
120		.sta_mask = PWR_STATUS_BDP,
121		.ctl_offs = SPM_BDP_PWR_CON,
122		.sram_pdn_bits = GENMASK(11, 8),
123	},
124	[MT7623_POWER_DOMAIN_ETH] = {
125		.sta_mask = PWR_STATUS_ETH,
126		.ctl_offs = SPM_ETH_PWR_CON,
127		.sram_pdn_bits = GENMASK(11, 8),
128		.sram_pdn_ack_bits = GENMASK(15, 12),
129	},
130	[MT7623_POWER_DOMAIN_HIF] = {
131		.sta_mask = PWR_STATUS_HIF,
132		.ctl_offs = SPM_HIF_PWR_CON,
133		.sram_pdn_bits = GENMASK(11, 8),
134		.sram_pdn_ack_bits = GENMASK(15, 12),
135	},
136	[MT7623_POWER_DOMAIN_IFR_MSC] = {
137		.sta_mask = PWR_STATUS_IFR_MSC,
138		.ctl_offs = SPM_IFR_MSC_PWR_CON,
139	},
140};
141
142static struct scp_domain_data scp_domain_mt7629[] = {
143	[MT7629_POWER_DOMAIN_ETHSYS] = {
144		.sta_mask = PWR_STATUS_ETHSYS,
145		.ctl_offs = SPM_ETHSYS_PWR_CON,
146		.sram_pdn_bits = GENMASK(11, 8),
147		.sram_pdn_ack_bits = GENMASK(15, 12),
148		.bus_prot_mask = (BIT(3) | BIT(17)),
149	},
150	[MT7629_POWER_DOMAIN_HIF0] = {
151		.sta_mask = PWR_STATUS_HIF0,
152		.ctl_offs = SPM_HIF0_PWR_CON,
153		.sram_pdn_bits = GENMASK(11, 8),
154		.sram_pdn_ack_bits = GENMASK(15, 12),
155		.bus_prot_mask = GENMASK(25, 24),
156	},
157	[MT7629_POWER_DOMAIN_HIF1] = {
158		.sta_mask = PWR_STATUS_HIF1,
159		.ctl_offs = SPM_HIF1_PWR_CON,
160		.sram_pdn_bits = GENMASK(11, 8),
161		.sram_pdn_ack_bits = GENMASK(15, 12),
162		.bus_prot_mask = GENMASK(28, 26),
163	},
164};
165
166/**
167 * This function enables the bus protection bits for disabled power
168 * domains so that the system does not hang when some unit accesses the
169 * bus while in power down.
170 */
171static int mtk_infracfg_set_bus_protection(void __iomem *infracfg,
172					   u32 mask)
173{
174	u32 val;
175
176	clrsetbits_le32(infracfg + INFRA_TOPAXI_PROT_EN, mask, mask);
177
178	return readl_poll_timeout(infracfg + INFRA_TOPAXI_PROT_STA1, val,
179				  (val & mask) == mask, 100);
180}
181
182static int mtk_infracfg_clear_bus_protection(void __iomem *infracfg,
183					     u32 mask)
184{
185	u32 val;
186
187	clrbits_le32(infracfg + INFRA_TOPAXI_PROT_EN, mask);
188
189	return readl_poll_timeout(infracfg + INFRA_TOPAXI_PROT_STA1, val,
190				  !(val & mask), 100);
191}
192
193static int scpsys_domain_is_on(struct scp_domain_data *data)
194{
195	struct scp_domain *scpd = data->scpd;
196	u32 sta = readl(scpd->base + SPM_PWR_STATUS) &
197			data->sta_mask;
198	u32 sta2 = readl(scpd->base + SPM_PWR_STATUS_2ND) &
199			 data->sta_mask;
200
201	/*
202	 * A domain is on when both status bits are set. If only one is set
203	 * return an error. This happens while powering up a domain
204	 */
205	if (sta && sta2)
206		return true;
207	if (!sta && !sta2)
208		return false;
209
210	return -EINVAL;
211}
212
213static int scpsys_power_on(struct power_domain *power_domain)
214{
215	struct scp_domain *scpd = dev_get_priv(power_domain->dev);
216	struct scp_domain_data *data = &scpd->data[power_domain->id];
217	void __iomem *ctl_addr = scpd->base + data->ctl_offs;
218	u32 pdn_ack = data->sram_pdn_ack_bits;
219	u32 val;
220	int ret, tmp;
221
222	writel(SPM_EN, scpd->base);
223
224	val = readl(ctl_addr);
225	val |= PWR_ON_BIT;
226	writel(val, ctl_addr);
227
228	val |= PWR_ON_2ND_BIT;
229	writel(val, ctl_addr);
230
231	ret = readx_poll_timeout(scpsys_domain_is_on, data, tmp, tmp > 0,
232				 100);
233	if (ret < 0)
234		return ret;
235
236	val &= ~PWR_CLK_DIS_BIT;
237	writel(val, ctl_addr);
238
239	val &= ~PWR_ISO_BIT;
240	writel(val, ctl_addr);
241
242	val |= PWR_RST_B_BIT;
243	writel(val, ctl_addr);
244
245	val &= ~data->sram_pdn_bits;
246	writel(val, ctl_addr);
247
248	ret = readl_poll_timeout(ctl_addr, tmp, !(tmp & pdn_ack), 100);
249	if (ret < 0)
250		return ret;
251
252	if (data->bus_prot_mask) {
253		ret = mtk_infracfg_clear_bus_protection(scpd->infracfg,
254							data->bus_prot_mask);
255		if (ret)
256			return ret;
257	}
258
259	return 0;
260}
261
262static int scpsys_power_off(struct power_domain *power_domain)
263{
264	struct scp_domain *scpd = dev_get_priv(power_domain->dev);
265	struct scp_domain_data *data = &scpd->data[power_domain->id];
266	void __iomem *ctl_addr = scpd->base + data->ctl_offs;
267	u32 pdn_ack = data->sram_pdn_ack_bits;
268	u32 val;
269	int ret, tmp;
270
271	if (data->bus_prot_mask) {
272		ret = mtk_infracfg_set_bus_protection(scpd->infracfg,
273						      data->bus_prot_mask);
274		if (ret)
275			return ret;
276	}
277
278	val = readl(ctl_addr);
279	val |= data->sram_pdn_bits;
280	writel(val, ctl_addr);
281
282	ret = readl_poll_timeout(ctl_addr, tmp, (tmp & pdn_ack) == pdn_ack,
283				 100);
284	if (ret < 0)
285		return ret;
286
287	val |= PWR_ISO_BIT;
288	writel(val, ctl_addr);
289
290	val &= ~PWR_RST_B_BIT;
291	writel(val, ctl_addr);
292
293	val |= PWR_CLK_DIS_BIT;
294	writel(val, ctl_addr);
295
296	val &= ~PWR_ON_BIT;
297	writel(val, ctl_addr);
298
299	val &= ~PWR_ON_2ND_BIT;
300	writel(val, ctl_addr);
301
302	ret = readx_poll_timeout(scpsys_domain_is_on, data, tmp, !tmp, 100);
303	if (ret < 0)
304		return ret;
305
306	return 0;
307}
308
309static int scpsys_power_request(struct power_domain *power_domain)
310{
311	struct scp_domain *scpd = dev_get_priv(power_domain->dev);
312	struct scp_domain_data *data;
313
314	data = &scpd->data[power_domain->id];
315	data->scpd = scpd;
316
317	return 0;
318}
319
320static int mtk_power_domain_hook(struct udevice *dev)
321{
322	struct scp_domain *scpd = dev_get_priv(dev);
323
324	scpd->type = (enum scp_domain_type)dev_get_driver_data(dev);
325
326	switch (scpd->type) {
327	case SCPSYS_MT7623:
328		scpd->data = scp_domain_mt7623;
329		break;
330	case SCPSYS_MT7622:
331	case SCPSYS_MT7629:
332		scpd->data = scp_domain_mt7629;
333		break;
334	default:
335		return -EINVAL;
336	}
337
338	return 0;
339}
340
341static int mtk_power_domain_probe(struct udevice *dev)
342{
343	struct ofnode_phandle_args args;
344	struct scp_domain *scpd = dev_get_priv(dev);
345	struct regmap *regmap;
346	struct clk_bulk bulk;
347	int err;
348
349	scpd->base = dev_read_addr_ptr(dev);
350	if (!scpd->base)
351		return -ENOENT;
352
353	err = mtk_power_domain_hook(dev);
354	if (err)
355		return err;
356
357	/* get corresponding syscon phandle */
358	err = dev_read_phandle_with_args(dev, "infracfg", NULL, 0, 0, &args);
359	if (err)
360		return err;
361
362	regmap = syscon_node_to_regmap(args.node);
363	if (IS_ERR(regmap))
364		return PTR_ERR(regmap);
365
366	scpd->infracfg = regmap_get_range(regmap, 0);
367	if (!scpd->infracfg)
368		return -ENOENT;
369
370	/* enable Infra DCM */
371	setbits_le32(scpd->infracfg + INFRA_TOPDCM_CTRL, DCM_TOP_EN);
372
373	err = clk_get_bulk(dev, &bulk);
374	if (err)
375		return err;
376
377	return clk_enable_bulk(&bulk);
378}
379
380static const struct udevice_id mtk_power_domain_ids[] = {
381	{
382		.compatible = "mediatek,mt7622-scpsys",
383		.data = SCPSYS_MT7622,
384	},
385	{
386		.compatible = "mediatek,mt7623-scpsys",
387		.data = SCPSYS_MT7623,
388	},
389	{
390		.compatible = "mediatek,mt7629-scpsys",
391		.data = SCPSYS_MT7629,
392	},
393	{ /* sentinel */ }
394};
395
396struct power_domain_ops mtk_power_domain_ops = {
397	.off = scpsys_power_off,
398	.on = scpsys_power_on,
399	.request = scpsys_power_request,
400};
401
402U_BOOT_DRIVER(mtk_power_domain) = {
403	.name = "mtk_power_domain",
404	.id = UCLASS_POWER_DOMAIN,
405	.ops = &mtk_power_domain_ops,
406	.probe = mtk_power_domain_probe,
407	.of_match = mtk_power_domain_ids,
408	.priv_auto	= sizeof(struct scp_domain),
409};
410