1/* $NetBSD: mesongxl_usb2phy.c,v 1.2 2021/01/27 03:10:18 thorpej Exp $ */
2
3/*-
4 * Copyright (c) 2019 Jared McNeill <jmcneill@invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30
31__KERNEL_RCSID(0, "$NetBSD: mesongxl_usb2phy.c,v 1.2 2021/01/27 03:10:18 thorpej Exp $");
32
33#include <sys/param.h>
34#include <sys/bus.h>
35#include <sys/device.h>
36#include <sys/intr.h>
37#include <sys/systm.h>
38#include <sys/time.h>
39
40#include <dev/fdt/fdtvar.h>
41
42#define	USB2PHY_REG0			0x00
43#define	 REG0_POWER_ON_RESET		__BIT(22)
44#define	 REG0_ID_PULLUP			__BIT(13)
45#define	 REG0_DP_PULLDOWN		__BIT(6)
46#define	 REG0_DM_PULLDOWN		__BIT(5)
47
48static int mesongxl_usb2phy_match(device_t, cfdata_t, void *);
49static void mesongxl_usb2phy_attach(device_t, device_t, void *);
50
51static const struct device_compatible_entry compat_data[] = {
52	{ .compat = "amlogic,meson-gxl-usb2-phy" },
53	DEVICE_COMPAT_EOL
54};
55
56struct mesongxl_usb2phy_softc {
57	device_t		sc_dev;
58	bus_space_tag_t		sc_bst;
59	bus_space_handle_t	sc_bsh;
60	int			sc_phandle;
61	struct clk		*sc_clk;
62	struct fdtbus_reset	*sc_rst;
63	struct fdtbus_regulator	*sc_supply;
64};
65
66#define	PHY_READ(sc, reg)				\
67	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
68#define	PHY_WRITE(sc, reg, val)			\
69	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
70
71CFATTACH_DECL_NEW(mesongxl_usb2phy, sizeof(struct mesongxl_usb2phy_softc),
72	mesongxl_usb2phy_match, mesongxl_usb2phy_attach, NULL, NULL);
73
74static void *
75mesongxl_usb2phy_acquire(device_t dev, const void *data, size_t len)
76{
77	if (len != 0)
78		return NULL;
79
80	return (void *)(uintptr_t)1;
81}
82
83static void
84mesongxl_usb2phy_release(device_t dev, void *priv)
85{
86}
87
88static int
89mesongxl_usb2phy_enable(device_t dev, void *priv, bool enable)
90{
91	struct mesongxl_usb2phy_softc * const sc = device_private(dev);
92	uint32_t val;
93
94	if (enable) {
95		/* Power on PHY */
96		val = PHY_READ(sc, USB2PHY_REG0);
97		val &= ~REG0_POWER_ON_RESET;
98		PHY_WRITE(sc, USB2PHY_REG0, val);
99
100		/* Configure PHY for host mode */
101		val = PHY_READ(sc, USB2PHY_REG0);
102		val |= REG0_DM_PULLDOWN;
103		val |= REG0_DP_PULLDOWN;
104		val &= ~REG0_ID_PULLUP;
105		PHY_WRITE(sc, USB2PHY_REG0, val);
106
107		/* Reset the PHY */
108		val = PHY_READ(sc, USB2PHY_REG0);
109		val |= REG0_POWER_ON_RESET;
110		PHY_WRITE(sc, USB2PHY_REG0, val);
111		delay(500);
112		val = PHY_READ(sc, USB2PHY_REG0);
113		val &= ~REG0_POWER_ON_RESET;
114		PHY_WRITE(sc, USB2PHY_REG0, val);
115		delay(500);
116
117		if (sc->sc_supply != NULL) {
118			if (fdtbus_regulator_enable(sc->sc_supply) != 0)
119				aprint_error(": couldn't enable supply\n");
120		}
121	} else {
122		/* Power off PHY */
123		val = PHY_READ(sc, USB2PHY_REG0);
124		val |= REG0_POWER_ON_RESET;
125		PHY_WRITE(sc, USB2PHY_REG0, val);
126	}
127
128	return 0;
129}
130
131const struct fdtbus_phy_controller_func mesongxl_usb2phy_funcs = {
132	.acquire = mesongxl_usb2phy_acquire,
133	.release = mesongxl_usb2phy_release,
134	.enable = mesongxl_usb2phy_enable,
135};
136
137static int
138mesongxl_usb2phy_match(device_t parent, cfdata_t cf, void *aux)
139{
140	struct fdt_attach_args * const faa = aux;
141
142	return of_compatible_match(faa->faa_phandle, compat_data);
143}
144
145static void
146mesongxl_usb2phy_attach(device_t parent, device_t self, void *aux)
147{
148	struct mesongxl_usb2phy_softc * const sc = device_private(self);
149	struct fdt_attach_args * const faa = aux;
150	const int phandle = faa->faa_phandle;
151	bus_addr_t addr;
152	bus_size_t size;
153
154	sc->sc_dev = self;
155	sc->sc_bst = faa->faa_bst;
156	sc->sc_phandle = phandle;
157
158	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
159		aprint_error(": couldn't get registers\n");
160		return;
161	}
162	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
163		aprint_error(": couldn't map registers\n");
164		return;
165	}
166
167	sc->sc_clk = fdtbus_clock_get_index(phandle, 0);
168	if (sc->sc_clk == NULL) {
169		aprint_error(": couldn't get clock\n");
170		return;
171	}
172	sc->sc_rst = fdtbus_reset_get_index(phandle, 0);
173
174	if (sc->sc_rst != NULL) {
175		if (fdtbus_reset_deassert(sc->sc_rst) != 0) {
176			aprint_error(": couldn't de-assert reset\n");
177			return;
178		}
179	}
180	if (clk_enable(sc->sc_clk) != 0) {
181		aprint_error(": couldn't enable clock\n");
182		return;
183	}
184
185	sc->sc_supply = fdtbus_regulator_acquire(phandle, "phy-supply");
186
187	aprint_naive("\n");
188	aprint_normal(": USB2 PHY\n");
189
190	fdtbus_register_phy_controller(self, phandle, &mesongxl_usb2phy_funcs);
191}
192