1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
4 *
5 * Author:  Weijie Gao <weijie.gao@mediatek.com>
6 *
7 * Misc driver for manipulating System control registers
8 */
9
10#include <dm.h>
11#include <misc.h>
12#include <asm/io.h>
13#include <asm/addrspace.h>
14#include <dm/device_compat.h>
15#include <mach/mt7620-sysc.h>
16#include "mt7620.h"
17
18struct mt7620_sysc_priv {
19	void __iomem *base;
20};
21
22static int mt7620_sysc_read(struct udevice *dev, int offset, void *buf,
23			    int size)
24{
25	struct mt7620_sysc_priv *priv = dev_get_priv(dev);
26	u32 val;
27
28	if (offset % sizeof(u32) || size != sizeof(u32) ||
29	    offset >= SYSCTL_SIZE)
30		return -EINVAL;
31
32	val = readl(priv->base + offset);
33
34	if (buf)
35		*(u32 *)buf = val;
36
37	return 0;
38}
39
40static int mt7620_sysc_write(struct udevice *dev, int offset, const void *buf,
41			     int size)
42{
43	struct mt7620_sysc_priv *priv = dev_get_priv(dev);
44	u32 val;
45
46	if (offset % sizeof(u32) || size != sizeof(u32) ||
47	    offset >= SYSCTL_SIZE || !buf)
48		return -EINVAL;
49
50	val = *(u32 *)buf;
51	writel(val, priv->base + offset);
52
53	return 0;
54}
55
56static int mt7620_sysc_ioctl(struct udevice *dev, unsigned long request,
57			     void *buf)
58{
59	struct mt7620_sysc_priv *priv = dev_get_priv(dev);
60	struct mt7620_sysc_chip_rev *chip_rev;
61	struct mt7620_sysc_clks *clks;
62	u32 val, shift;
63
64	if (!buf)
65		return -EINVAL;
66
67	switch (request) {
68	case MT7620_SYSC_IOCTL_GET_CLK:
69		clks = buf;
70		mt7620_get_clks(&clks->cpu_clk, &clks->sys_clk,
71				&clks->xtal_clk);
72
73		val = readl(priv->base + SYSCTL_CLKCFG0_REG);
74		if (val & PERI_CLK_SEL)
75			clks->peri_clk = clks->xtal_clk;
76		else
77			clks->peri_clk = 40000000;
78
79		return 0;
80
81	case MT7620_SYSC_IOCTL_GET_CHIP_REV:
82		chip_rev = buf;
83
84		val = readl(priv->base + SYSCTL_CHIP_REV_ID_REG);
85
86		chip_rev->bga = !!(val & PKG_ID);
87		chip_rev->ver = (val & VER_M) >> VER_S;
88		chip_rev->eco = (val & ECO_M) >> ECO_S;
89
90		return 0;
91
92	case MT7620_SYSC_IOCTL_SET_GE1_MODE:
93	case MT7620_SYSC_IOCTL_SET_GE2_MODE:
94		val = *(u32 *)buf;
95
96		if (val > MT7620_SYSC_GE_ESW_PHY)
97			return -EINVAL;
98
99		if (request == MT7620_SYSC_IOCTL_SET_GE1_MODE)
100			shift = GE1_MODE_S;
101		else
102			shift = GE2_MODE_S;
103
104		clrsetbits_32(priv->base + SYSCTL_SYSCFG1_REG,
105			      GE_MODE_M << shift, val << shift);
106
107		return 0;
108
109	case MT7620_SYSC_IOCTL_SET_USB_MODE:
110		val = *(u32 *)buf;
111
112		if (val == MT7620_SYSC_USB_DEVICE_MODE)
113			val = 0;
114		else if (val == MT7620_SYSC_USB_HOST_MODE)
115			val = USB0_HOST_MODE;
116
117		clrsetbits_32(priv->base + SYSCTL_SYSCFG1_REG,
118			      USB0_HOST_MODE, val);
119
120		return 0;
121
122	case MT7620_SYSC_IOCTL_SET_PCIE_MODE:
123		val = *(u32 *)buf;
124
125		if (val == MT7620_SYSC_PCIE_EP_MODE)
126			val = 0;
127		else if (val == MT7620_SYSC_PCIE_RC_MODE)
128			val = PCIE_RC_MODE;
129
130		clrsetbits_32(priv->base + SYSCTL_SYSCFG1_REG,
131			      PCIE_RC_MODE, val);
132
133		return 0;
134
135	default:
136		return -EINVAL;
137	}
138}
139
140static int mt7620_sysc_probe(struct udevice *dev)
141{
142	struct mt7620_sysc_priv *priv = dev_get_priv(dev);
143
144	priv->base = dev_remap_addr_index(dev, 0);
145	if (!priv->base) {
146		dev_err(dev, "failed to map sysc registers\n");
147		return -EINVAL;
148	}
149
150	return 0;
151}
152
153static struct misc_ops mt7620_sysc_ops = {
154	.read = mt7620_sysc_read,
155	.write = mt7620_sysc_write,
156	.ioctl = mt7620_sysc_ioctl,
157};
158
159static const struct udevice_id mt7620_sysc_ids[] = {
160	{ .compatible = "mediatek,mt7620-sysc" },
161	{ }
162};
163
164U_BOOT_DRIVER(mt7620_sysc) = {
165	.name		= "mt7620_sysc",
166	.id		= UCLASS_MISC,
167	.of_match	= mt7620_sysc_ids,
168	.probe		= mt7620_sysc_probe,
169	.ops		= &mt7620_sysc_ops,
170	.priv_auto	= sizeof(struct mt7620_sysc_priv),
171	.flags = DM_FLAG_PRE_RELOC,
172};
173