summaryrefslogtreecommitdiff
path: root/sys/dev/fdt/amldwusb.c
blob: a13dedd0fd15d2377599508f8471b0833c709dae (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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/*	$OpenBSD: amldwusb.c,v 1.1 2019/08/29 17:20:03 kettenis Exp $	*/
/*
 * Copyright (c) 2019 Mark kettenis <kettenis@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>

#include <machine/bus.h>
#include <machine/fdt.h>

#include <arm64/dev/simplebusvar.h>

#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_clock.h>
#include <dev/ofw/ofw_misc.h>
#include <dev/ofw/ofw_regulator.h>
#include <dev/ofw/fdt.h>

/* Glue registers. */

#define U2P_R0(i)			(0x00 + (i) * 0x20)
#define  U2P_R0_HOST_DEVICE			(1 << 0)
#define  U2P_R0_POWER_OK			(1 << 1)
#define  U2P_R0_HAST_MODE			(1 << 2)
#define  U2P_R0_POWER_ON_RESET			(1 << 3)
#define  U2P_R0_ID_PULLUP			(1 << 4)
#define  U2P_R0_DRV_VBUS			(1 << 5)
#define U2P_R1(i)			(0x04 + (i) * 0x20)
#define  U2P_R1_PHY_READY			(1 << 0)
#define  U2P_R1_ID_DIG				(1 << 1)
#define  U2P_R1_OTG_SESSION_VALID		(1 << 2)
#define  U2P_R1_VBUS_VALID			(1 << 3)

#define USB_R0				0x80
#define  USB_R0_P30_LANE0_TX2RX_LOOPBACK	(1 << 17)
#define  USB_R0_P30_LANE0_EXT_PCLK_REQ		(1 << 18)
#define  USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK	(0x3ff << 19)
#define  USB_R0_P30_PCS_RX_LOS_MASK_VAL_SHIFT	19
#define  USB_R0_U2D_SS_SCALEDOWN_MODE_MASK	(0x3 << 29)
#define  USB_R0_U2D_SS_SCALEDOWN_MODE_SHIFT	29
#define  USB_R0_U2D_ACT				(1U << 31)
#define USB_R1				0x84
#define  USB_R1_U3H_BIGENDIAN_GS		(1 << 0)
#define  USB_R1_U3H_PME_ENABLE			(1 << 1)
#define  USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK	(0x7 << 2)
#define  USB_R1_U3H_HUB_PORT_OVERCURRENT_SHIFT	2
#define  USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK	(0x7 << 7)
#define  USB_R1_U3H_HUB_PORT_PERM_ATTACH_SHIFT	7
#define  USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK	(0x3 << 12)
#define  USB_R1_U3H_HOST_U2_PORT_DISABLE_SHIFT	12
#define  USB_R1_U3H_HOST_U3_PORT_DISABLE	(1 << 16)
#define  USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT (1 << 17)
#define  USB_R1_U3H_HOST_MSI_ENABLE		(1 << 18)
#define  USB_R1_U3H_FLADJ_30MHZ_REG_MASK	(0x3f << 19)
#define  USB_R1_U3H_FLADJ_30MHZ_REG_SHIFT	19
#define  USB_R1_P30_PCS_TX_SWING_FULL_MASK	(0x7f << 25)
#define  USB_R1_P30_PCS_TX_SWING_FULL_SHIFT	25
#define USB_R2				0x88
#define  USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK	(0x3f << 20)
#define  USB_R2_P30_PCS_TX_DEEMPH_3P5DB_SHIFT	20
#define  USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK	(0x3f << 26)
#define  USB_R2_P30_PCS_TX_DEEMPH_6DB_SHIFT	26
#define USB_R3				0x8c
#define  USB_R3_P30_SSC_ENABLE			(1 << 0)
#define  USB_R3_P30_SSC_RANGE_MASK		(0x7 << 1)
#define  USB_R3_P30_SSC_RANGE_SHIFT		1
#define  USB_R3_P30_SSC_REF_CLK_SEL_MASK	(0x1ff << 4)
#define  USB_R3_P30_SSC_REF_CLK_SEL_SHIFT	4
#define  USB_R3_P30_REF_SSP_EN			(1 << 13)
#define USB_R4				0x90
#define  USB_R4_P21_PORT_RESET_0		(1 << 0)
#define  USB_R4_P21_SLEEP_M0			(1 << 1)
#define  USB_R4_MEM_PD_MASK			(0x3 << 2)
#define  USB_R4_MEM_PD_SHIFT			2
#define  USB_R4_P21_ONLY			(1 << 4)
#define USB_R5				0x94
#define  USB_R5_ID_DIG_SYNC			(1 << 0)
#define  USB_R5_ID_DIG_REG			(1 << 1)
#define  USB_R5_ID_DIG_CFG_MASK			(0x3 << 2)
#define  USB_R5_ID_DIG_CFG_SHIFT		2
#define  USB_R5_ID_DIG_EN_0			(1 << 4)
#define  USB_R5_ID_DIG_EN_1			(1 << 5)
#define  USB_R5_ID_DIG_CURR			(1 << 6)
#define  USB_R5_ID_DIG_IRQ			(1 << 7)
#define  USB_R5_ID_DIG_TH_MASK			(0xff << 8)
#define  USB_R5_ID_DIG_TH_SHIFT			8
#define  USB_R5_ID_DIG_CNT_MASK			(0xff << 16)
#define  USB_R5_ID_DIG_CNT_SHIFT		16

#define HREAD4(sc, reg)							\
	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
#define HWRITE4(sc, reg, val)						\
	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
#define HSET4(sc, reg, bits)						\
	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
#define HCLR4(sc, reg, bits)						\
	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))

struct amldwusb_softc {
	struct simplebus_softc	sc_sbus;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
};

int	amldwusb_match(struct device *, void *, void *);
void	amldwusb_attach(struct device *, struct device *, void *);

struct cfattach amldwusb_ca = {
	sizeof(struct amldwusb_softc), amldwusb_match, amldwusb_attach
};

struct cfdriver amldwusb_cd = {
	NULL, "amldwusb", DV_DULL
};

void	amldwusb_init_usb2(struct amldwusb_softc *);
void	amldwusb_init_usb3(struct amldwusb_softc *);
void	amldwusb_init_phys(struct amldwusb_softc *);

int
amldwusb_match(struct device *parent, void *match, void *aux)
{
	struct fdt_attach_args *faa = aux;

	return OF_is_compatible(faa->fa_node, "amlogic,meson-g12a-usb-ctrl");
}

void
amldwusb_attach(struct device *parent, struct device *self, void *aux)
{
	struct amldwusb_softc *sc = (struct amldwusb_softc *)self;
	struct fdt_attach_args *faa = aux;
	uint32_t vbus_supply;
	uint32_t reg;

	if (faa->fa_nreg < 1) {
		printf(": no registers\n");
		return;
	}

	sc->sc_iot = faa->fa_iot;
	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
		printf(": can't map registers\n");
		return;
	}

	clock_enable_all(faa->fa_node);

	reset_assert_all(faa->fa_node);
	delay(10);
	reset_deassert_all(faa->fa_node);

	vbus_supply = OF_getpropint(faa->fa_node, "vbus-supply", 0);
	if (vbus_supply)
		regulator_enable(vbus_supply);

	amldwusb_init_usb2(sc);

	reg = HREAD4(sc, USB_R1);
	reg &= ~USB_R1_U3H_FLADJ_30MHZ_REG_MASK;
	reg |= (0x20 << USB_R1_U3H_FLADJ_30MHZ_REG_SHIFT);
	HWRITE4(sc, USB_R1, reg);

	HSET4(sc, USB_R5, USB_R5_ID_DIG_EN_0);
	HSET4(sc, USB_R5, USB_R5_ID_DIG_EN_1);
	reg = HREAD4(sc, USB_R5);
	reg &= ~USB_R5_ID_DIG_TH_MASK;
	reg |= (0xff << USB_R5_ID_DIG_TH_SHIFT);
	HWRITE4(sc, USB_R5, reg);

	amldwusb_init_usb3(sc);

	/* Initialize PHYs. */
	phy_enable(faa->fa_node, "usb2-phy0");
	phy_enable(faa->fa_node, "usb2-phy1");
	phy_enable(faa->fa_node, "usb3-phy0");

	simplebus_attach(parent, &sc->sc_sbus.sc_dev, faa);
}

void
amldwusb_init_usb2(struct amldwusb_softc *sc)
{
	int i;

	for (i = 0; i < 3; i++) {
		HSET4(sc, U2P_R0(i), U2P_R0_POWER_ON_RESET);

		/* We don't support device mode, so always force host mode. */
		HSET4(sc, U2P_R0(i), U2P_R0_HOST_DEVICE);

		HCLR4(sc, U2P_R0(i), U2P_R0_POWER_ON_RESET);
	}
}

void
amldwusb_init_usb3(struct amldwusb_softc *sc)
{
	uint32_t reg;

	reg = HREAD4(sc, USB_R3);
	reg &= ~USB_R3_P30_SSC_RANGE_MASK;
	reg |= USB_R3_P30_SSC_ENABLE;
	reg |= (2 << USB_R3_P30_SSC_RANGE_SHIFT);
	reg |= USB_R3_P30_REF_SSP_EN;
	HWRITE4(sc, USB_R3, reg);

	delay(2);

	reg = HREAD4(sc, USB_R2);
	reg &= ~USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK;
	reg |= (0x15 << USB_R2_P30_PCS_TX_DEEMPH_3P5DB_SHIFT);
	HWRITE4(sc, USB_R2, reg);
	reg = HREAD4(sc, USB_R2);
	reg &= ~USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK;
	reg |= (0x15 << USB_R2_P30_PCS_TX_DEEMPH_6DB_SHIFT);
	HWRITE4(sc, USB_R2, reg);

	delay(2);

	HSET4(sc, USB_R1, USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT);
	reg = HREAD4(sc, USB_R1);
	reg &= ~USB_R1_P30_PCS_TX_SWING_FULL_MASK;
	reg |= (0x7f << USB_R1_P30_PCS_TX_SWING_FULL_SHIFT);
	HWRITE4(sc, USB_R1, reg);
}