1296284Sjmcneill/*-
2296284Sjmcneill * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
3296284Sjmcneill * All rights reserved.
4296284Sjmcneill *
5296284Sjmcneill * Redistribution and use in source and binary forms, with or without
6296284Sjmcneill * modification, are permitted provided that the following conditions
7296284Sjmcneill * are met:
8296284Sjmcneill * 1. Redistributions of source code must retain the above copyright
9296284Sjmcneill *    notice, this list of conditions and the following disclaimer.
10296284Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
11296284Sjmcneill *    notice, this list of conditions and the following disclaimer in the
12296284Sjmcneill *    documentation and/or other materials provided with the distribution.
13296284Sjmcneill *
14296284Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15296284Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16296284Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17296284Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18296284Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19296284Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20296284Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21296284Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22296284Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23296284Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24296284Sjmcneill * SUCH DAMAGE.
25296284Sjmcneill *
26296284Sjmcneill * $FreeBSD: stable/11/sys/arm/allwinner/aw_usbphy.c 332025 2018-04-04 13:23:06Z mmel $
27296284Sjmcneill */
28296284Sjmcneill
29296284Sjmcneill/*
30296284Sjmcneill * Allwinner USB PHY
31296284Sjmcneill */
32296284Sjmcneill
33296284Sjmcneill#include <sys/cdefs.h>
34296284Sjmcneill__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/aw_usbphy.c 332025 2018-04-04 13:23:06Z mmel $");
35296284Sjmcneill
36296284Sjmcneill#include <sys/param.h>
37296284Sjmcneill#include <sys/systm.h>
38296284Sjmcneill#include <sys/bus.h>
39296284Sjmcneill#include <sys/rman.h>
40296284Sjmcneill#include <sys/kernel.h>
41296284Sjmcneill#include <sys/module.h>
42296284Sjmcneill#include <sys/gpio.h>
43296284Sjmcneill
44296284Sjmcneill#include <dev/ofw/ofw_bus.h>
45296284Sjmcneill#include <dev/ofw/ofw_bus_subr.h>
46300728Sjmcneill#include <dev/gpio/gpiobusvar.h>
47296284Sjmcneill
48297627Sjmcneill#include <dev/extres/clk/clk.h>
49297627Sjmcneill#include <dev/extres/hwreset/hwreset.h>
50297627Sjmcneill#include <dev/extres/regulator/regulator.h>
51300728Sjmcneill#include <dev/extres/phy/phy.h>
52296284Sjmcneill
53332025Smmel#include "phynode_if.h"
54296284Sjmcneill
55300728Sjmcneill#define	USBPHY_NPHYS	4
56300728Sjmcneill
57296284Sjmcneillstatic struct ofw_compat_data compat_data[] = {
58296284Sjmcneill	{ "allwinner,sun4i-a10-usb-phy",	1 },
59296284Sjmcneill	{ "allwinner,sun5i-a13-usb-phy",	1 },
60296284Sjmcneill	{ "allwinner,sun6i-a31-usb-phy",	1 },
61296284Sjmcneill	{ "allwinner,sun7i-a20-usb-phy",	1 },
62299113Sjmcneill	{ "allwinner,sun8i-a83t-usb-phy",	1 },
63299688Smanu	{ "allwinner,sun8i-h3-usb-phy",		1 },
64296284Sjmcneill	{ NULL,					0 }
65296284Sjmcneill};
66296284Sjmcneill
67300728Sjmcneillstruct awusbphy_softc {
68300728Sjmcneill	regulator_t		reg[USBPHY_NPHYS];
69300728Sjmcneill	gpio_pin_t		id_det_pin;
70300728Sjmcneill	int			id_det_valid;
71300728Sjmcneill	gpio_pin_t		vbus_det_pin;
72300728Sjmcneill	int			vbus_det_valid;
73300728Sjmcneill};
74300728Sjmcneill
75332025Smmel /* Phy class and methods. */
76332025Smmelstatic int awusbphy_phy_enable(struct phynode *phy, bool enable);
77332025Smmelstatic phynode_method_t awusbphy_phynode_methods[] = {
78332025Smmel	PHYNODEMETHOD(phynode_enable, awusbphy_phy_enable),
79332025Smmel
80332025Smmel	PHYNODEMETHOD_END
81332025Smmel};
82332025SmmelDEFINE_CLASS_1(awusbphy_phynode, awusbphy_phynode_class, awusbphy_phynode_methods,
83332025Smmel    0, phynode_class);
84332025Smmel
85296284Sjmcneillstatic int
86296284Sjmcneillawusbphy_init(device_t dev)
87296284Sjmcneill{
88300728Sjmcneill	struct awusbphy_softc *sc;
89300728Sjmcneill	phandle_t node;
90296284Sjmcneill	char pname[20];
91296284Sjmcneill	int error, off;
92297627Sjmcneill	regulator_t reg;
93297627Sjmcneill	hwreset_t rst;
94297627Sjmcneill	clk_t clk;
95296284Sjmcneill
96300728Sjmcneill	sc = device_get_softc(dev);
97300728Sjmcneill	node = ofw_bus_get_node(dev);
98300728Sjmcneill
99297627Sjmcneill	/* Enable clocks */
100308324Smmel	for (off = 0; clk_get_by_ofw_index(dev, 0, off, &clk) == 0; off++) {
101297627Sjmcneill		error = clk_enable(clk);
102297627Sjmcneill		if (error != 0) {
103297627Sjmcneill			device_printf(dev, "couldn't enable clock %s\n",
104297627Sjmcneill			    clk_get_name(clk));
105296284Sjmcneill			return (error);
106297627Sjmcneill		}
107297627Sjmcneill	}
108296284Sjmcneill
109297627Sjmcneill	/* De-assert resets */
110308324Smmel	for (off = 0; hwreset_get_by_ofw_idx(dev, 0, off, &rst) == 0; off++) {
111297627Sjmcneill		error = hwreset_deassert(rst);
112297627Sjmcneill		if (error != 0) {
113297627Sjmcneill			device_printf(dev, "couldn't de-assert reset %d\n",
114297627Sjmcneill			    off);
115296284Sjmcneill			return (error);
116297627Sjmcneill		}
117297627Sjmcneill	}
118296284Sjmcneill
119300728Sjmcneill	/* Get regulators */
120300728Sjmcneill	for (off = 0; off < USBPHY_NPHYS; off++) {
121296284Sjmcneill		snprintf(pname, sizeof(pname), "usb%d_vbus-supply", off);
122308324Smmel		if (regulator_get_by_ofw_property(dev, 0, pname, &reg) == 0)
123300728Sjmcneill			sc->reg[off] = reg;
124300728Sjmcneill	}
125300728Sjmcneill
126300728Sjmcneill	/* Get GPIOs */
127300728Sjmcneill	error = gpio_pin_get_by_ofw_property(dev, node, "usb0_id_det-gpios",
128300728Sjmcneill	    &sc->id_det_pin);
129300728Sjmcneill	if (error == 0)
130300728Sjmcneill		sc->id_det_valid = 1;
131300728Sjmcneill	error = gpio_pin_get_by_ofw_property(dev, node, "usb0_vbus_det-gpios",
132300728Sjmcneill	    &sc->vbus_det_pin);
133300728Sjmcneill	if (error == 0)
134300728Sjmcneill		sc->vbus_det_valid = 1;
135300728Sjmcneill
136300728Sjmcneill	return (0);
137300728Sjmcneill}
138300728Sjmcneill
139300728Sjmcneillstatic int
140300728Sjmcneillawusbphy_vbus_detect(device_t dev, int *val)
141300728Sjmcneill{
142300728Sjmcneill	struct awusbphy_softc *sc;
143300728Sjmcneill	bool active;
144300728Sjmcneill	int error;
145300728Sjmcneill
146300728Sjmcneill	sc = device_get_softc(dev);
147300728Sjmcneill
148300728Sjmcneill	if (sc->vbus_det_valid) {
149300728Sjmcneill		error = gpio_pin_is_active(sc->vbus_det_pin, &active);
150300728Sjmcneill		if (error != 0)
151296284Sjmcneill			return (error);
152300728Sjmcneill		*val = active;
153300728Sjmcneill		return (0);
154296284Sjmcneill	}
155296284Sjmcneill
156300728Sjmcneill	*val = 1;
157296284Sjmcneill	return (0);
158296284Sjmcneill}
159296284Sjmcneill
160296284Sjmcneillstatic int
161332025Smmelawusbphy_phy_enable(struct phynode *phynode, bool enable)
162300728Sjmcneill{
163332025Smmel	device_t dev;
164332025Smmel	intptr_t phy;
165300728Sjmcneill	struct awusbphy_softc *sc;
166300728Sjmcneill	regulator_t reg;
167300728Sjmcneill	int error, vbus_det;
168300728Sjmcneill
169332025Smmel	dev = phynode_get_device(phynode);
170332025Smmel	phy = phynode_get_id(phynode);
171332025Smmel	sc = device_get_softc(dev);
172332025Smmel
173300728Sjmcneill	if (phy < 0 || phy >= USBPHY_NPHYS)
174300728Sjmcneill		return (ERANGE);
175300728Sjmcneill
176300728Sjmcneill	sc = device_get_softc(dev);
177300728Sjmcneill
178300728Sjmcneill	/* Regulators are optional. If not found, return success. */
179300728Sjmcneill	reg = sc->reg[phy];
180300728Sjmcneill	if (reg == NULL)
181300728Sjmcneill		return (0);
182300728Sjmcneill
183300728Sjmcneill	if (enable) {
184300728Sjmcneill		/* If an external vbus is detected, do not enable phy 0 */
185300728Sjmcneill		if (phy == 0) {
186300728Sjmcneill			error = awusbphy_vbus_detect(dev, &vbus_det);
187300728Sjmcneill			if (error == 0 && vbus_det == 1)
188300728Sjmcneill				return (0);
189300728Sjmcneill		} else
190300728Sjmcneill			error = 0;
191300728Sjmcneill		if (error == 0)
192300728Sjmcneill			error = regulator_enable(reg);
193300728Sjmcneill	} else
194300728Sjmcneill		error = regulator_disable(reg);
195300728Sjmcneill	if (error != 0) {
196309760Smanu		device_printf(dev,
197309760Smanu		    "couldn't %s regulator for phy %jd\n",
198309760Smanu		    enable ? "enable" : "disable", (intmax_t)phy);
199300728Sjmcneill		return (error);
200300728Sjmcneill	}
201300728Sjmcneill
202300728Sjmcneill	return (0);
203300728Sjmcneill}
204300728Sjmcneill
205300728Sjmcneillstatic int
206296284Sjmcneillawusbphy_probe(device_t dev)
207296284Sjmcneill{
208296284Sjmcneill	if (!ofw_bus_status_okay(dev))
209296284Sjmcneill		return (ENXIO);
210296284Sjmcneill
211296284Sjmcneill	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
212296284Sjmcneill		return (ENXIO);
213296284Sjmcneill
214296284Sjmcneill	device_set_desc(dev, "Allwinner USB PHY");
215296284Sjmcneill	return (BUS_PROBE_DEFAULT);
216296284Sjmcneill}
217296284Sjmcneill
218296284Sjmcneillstatic int
219296284Sjmcneillawusbphy_attach(device_t dev)
220296284Sjmcneill{
221296284Sjmcneill	int error;
222332025Smmel	struct phynode *phynode;
223332025Smmel	struct phynode_init_def phy_init;
224332025Smmel	int i;
225296284Sjmcneill
226296284Sjmcneill	error = awusbphy_init(dev);
227300728Sjmcneill	if (error) {
228296284Sjmcneill		device_printf(dev, "failed to initialize USB PHY, error %d\n",
229296284Sjmcneill		    error);
230300728Sjmcneill		return (error);
231300728Sjmcneill	}
232296284Sjmcneill
233332025Smmel	/* Create and register phys. */
234332025Smmel	for (i = 0; i < USBPHY_NPHYS; i++) {
235332025Smmel		bzero(&phy_init, sizeof(phy_init));
236332025Smmel		phy_init.id = i;
237332025Smmel		phy_init.ofw_node = ofw_bus_get_node(dev);
238332025Smmel		phynode = phynode_create(dev, &awusbphy_phynode_class,
239332025Smmel		    &phy_init);
240332025Smmel		if (phynode == NULL) {
241332025Smmel			device_printf(dev, "failed to create USB PHY\n");
242332025Smmel			return (ENXIO);
243332025Smmel		}
244332025Smmel		if (phynode_register(phynode) == NULL) {
245332025Smmel			device_printf(dev, "failed to create USB PHY\n");
246332025Smmel			return (ENXIO);
247332025Smmel		}
248332025Smmel	}
249300728Sjmcneill
250296284Sjmcneill	return (error);
251296284Sjmcneill}
252296284Sjmcneill
253296284Sjmcneillstatic device_method_t awusbphy_methods[] = {
254296284Sjmcneill	/* Device interface */
255296284Sjmcneill	DEVMETHOD(device_probe,		awusbphy_probe),
256296284Sjmcneill	DEVMETHOD(device_attach,	awusbphy_attach),
257296284Sjmcneill
258296284Sjmcneill	DEVMETHOD_END
259296284Sjmcneill};
260296284Sjmcneill
261296284Sjmcneillstatic driver_t awusbphy_driver = {
262296284Sjmcneill	"awusbphy",
263296284Sjmcneill	awusbphy_methods,
264300728Sjmcneill	sizeof(struct awusbphy_softc)
265296284Sjmcneill};
266296284Sjmcneill
267296284Sjmcneillstatic devclass_t awusbphy_devclass;
268296284Sjmcneill
269300728SjmcneillEARLY_DRIVER_MODULE(awusbphy, simplebus, awusbphy_driver, awusbphy_devclass,
270300728Sjmcneill    0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
271296284SjmcneillMODULE_VERSION(awusbphy, 1);
272