summaryrefslogtreecommitdiff
path: root/drivers/phy/phy-npcm-usb.c
blob: 24eba6655435c763808f5271d92c9dc0d3983155 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (c) 2021 Nuvoton Technology Corp.
 */

#include <common.h>
#include <dm.h>
#include <generic-phy.h>
#include <regmap.h>
#include <reset.h>
#include <syscon.h>
#include <dm/device_compat.h>
#include <linux/bitfield.h>
#include <linux/delay.h>

/* GCR Register Offsets */
#define GCR_INTCR3	0x9C
#define GCR_USB1PHYCTL	0x140
#define GCR_USB2PHYCTL	0x144
#define GCR_USB3PHYCTL	0x148

/* USBnPHYCTL bit fields */
#define PHYCTL_RS	BIT(28)

#define USBPHY2SW	GENMASK(13, 12)
#define USBPHY3SW	GENMASK(15, 14)

#define USBPHY2SW_DEV9_PHY1	FIELD_PREP(USBPHY2SW, 0)
#define USBPHY2SW_HOST1		FIELD_PREP(USBPHY2SW, 1)
#define USBPHY2SW_DEV9_PHY2	FIELD_PREP(USBPHY2SW, 3)
#define USBPHY3SW_DEV8_PHY1	FIELD_PREP(USBPHY3SW, 0)
#define USBPHY3SW_HOST2		FIELD_PREP(USBPHY3SW, 1)
#define USBPHY3SW_DEV8_PHY3	FIELD_PREP(USBPHY3SW, 3)

enum controller_id {
	UDC0_7,
	UDC8,
	UDC9,
	USBH1,
	USBH2,
};

enum phy_id {
	PHY1 = 1,
	PHY2,
	PHY3,
};

/* Phy Switch Settings */
#define USBDPHY1	((PHY1 << 8) | UDC0_7)	/* Connect UDC0~7 to PHY1 */
#define USBD8PHY1	((PHY1 << 8) | UDC8)	/* Connect UDC8 to PHY1 */
#define USBD9PHY1	((PHY1 << 8) | UDC9)	/* Connect UDC9 to PHY1 */
#define USBD9PHY2	((PHY2 << 8) | UDC9)	/* Connect UDC9 to PHY2 */
#define USBH1PHY2	((PHY2 << 8) | USBH1)	/* Connect USBH1 to PHY2 */
#define USBD8PHY3	((PHY3 << 8) | UDC8)	/* Connect UDC8 to PHY3 */
#define USBH2PHY3	((PHY3 << 8) | USBH2)	/* Connect USBH2 to PHY3 */

struct npcm_usbphy {
	struct regmap *syscon;
	u8 id;
	u16 phy_switch;	/* (phy_id << 8) | controller_id */
};

static int npcm_usb_phy_init(struct phy *phy)
{
	struct npcm_usbphy *priv = dev_get_priv(phy->dev);
	struct reset_ctl reset;
	int ret;

	ret = reset_get_by_index(phy->dev, 0, &reset);
	if (ret && ret != -ENOENT && ret != -ENOTSUPP) {
		dev_err(phy->dev, "can't get phy reset ctrl (err %d)", ret);
		return ret;
	}

	/* setup PHY switch */
	switch (priv->phy_switch) {
	case USBD8PHY1:
		regmap_update_bits(priv->syscon, GCR_INTCR3, USBPHY3SW,
				   USBPHY3SW_DEV8_PHY1);
		break;
	case USBD8PHY3:
		regmap_update_bits(priv->syscon, GCR_INTCR3, USBPHY3SW,
				   USBPHY3SW_DEV8_PHY3);
		break;
	case USBD9PHY1:
		regmap_update_bits(priv->syscon, GCR_INTCR3, USBPHY2SW,
				   USBPHY2SW_DEV9_PHY1);
		break;
	case USBD9PHY2:
		regmap_update_bits(priv->syscon, GCR_INTCR3, USBPHY2SW,
				   USBPHY2SW_DEV9_PHY2);
		break;
	case USBH1PHY2:
		regmap_update_bits(priv->syscon, GCR_INTCR3, USBPHY2SW,
				   USBPHY2SW_HOST1);
		break;
	case USBH2PHY3:
		regmap_update_bits(priv->syscon, GCR_INTCR3, USBPHY3SW,
				   USBPHY3SW_HOST2);
		break;
	default:
		break;
	}
	/* reset phy */
	if (reset_valid(&reset))
		reset_assert(&reset);

	/* Wait for PHY clocks to stablize for 50us or more */
	udelay(100);

	/* release phy from reset */
	if (reset_valid(&reset))
		reset_deassert(&reset);

	/* PHY RS bit should be set after reset */
	switch (priv->id) {
	case PHY1:
		regmap_update_bits(priv->syscon, GCR_USB1PHYCTL, PHYCTL_RS, PHYCTL_RS);
		break;
	case PHY2:
		regmap_update_bits(priv->syscon, GCR_USB2PHYCTL, PHYCTL_RS, PHYCTL_RS);
		break;
	case PHY3:
		regmap_update_bits(priv->syscon, GCR_USB3PHYCTL, PHYCTL_RS, PHYCTL_RS);
		break;
	default:
		break;
	}

	return 0;
}

static int npcm_usb_phy_exit(struct phy *phy)
{
	struct npcm_usbphy *priv = dev_get_priv(phy->dev);

	/* set PHY switch to default state */
	switch (priv->phy_switch) {
	case USBD8PHY1:
	case USBD8PHY3:
		regmap_update_bits(priv->syscon, GCR_INTCR3, USBPHY3SW,
				   USBPHY3SW_HOST2);
		break;
	case USBD9PHY1:
	case USBD9PHY2:
		regmap_update_bits(priv->syscon, GCR_INTCR3, USBPHY2SW,
				   USBPHY2SW_HOST1);
		break;
	default:
		break;
	}
	return 0;
}

static int  npcm_usb_phy_xlate(struct phy *phy, struct ofnode_phandle_args *args)
{
	struct npcm_usbphy *priv = dev_get_priv(phy->dev);
	u16 phy_switch;

	if (args->args_count < 1 || args->args[0] > USBH2)
		return -EINVAL;

	phy_switch = (priv->id << 8) | args->args[0];
	switch (phy_switch) {
	case USBD9PHY1:
	case USBH2PHY3:
	case USBD8PHY3:
		if (!IS_ENABLED(CONFIG_ARCH_NPCM8XX))
			return -EINVAL;
	case USBDPHY1:
	case USBD8PHY1:
	case USBD9PHY2:
	case USBH1PHY2:
		priv->phy_switch = phy_switch;
		return 0;
	default:
		return -EINVAL;
	}
}

static int npcm_usb_phy_probe(struct udevice *dev)
{
	struct npcm_usbphy *priv = dev_get_priv(dev);

	priv->syscon = syscon_regmap_lookup_by_phandle(dev->parent, "syscon");
	if (IS_ERR(priv->syscon)) {
		dev_err(dev, "%s: unable to get syscon\n", __func__);
		return PTR_ERR(priv->syscon);
	}
	priv->id = dev_read_u32_default(dev, "reg", -1);

	return 0;
}

static const struct udevice_id npcm_phy_ids[] = {
	{ .compatible = "nuvoton,npcm845-usb-phy",},
	{ .compatible = "nuvoton,npcm750-usb-phy",},
	{ }
};

static struct phy_ops npcm_phy_ops = {
	.init = npcm_usb_phy_init,
	.exit = npcm_usb_phy_exit,
	.of_xlate = npcm_usb_phy_xlate,
};

U_BOOT_DRIVER(npcm_phy) = {
	.name	= "npcm-usb-phy",
	.id	= UCLASS_PHY,
	.of_match = npcm_phy_ids,
	.ops = &npcm_phy_ops,
	.probe		= npcm_usb_phy_probe,
	.priv_auto	= sizeof(struct npcm_usbphy),
};