xref: /freebsd/sys/arm/allwinner/aw_usbphy.c (revision 84ebe16ac69b56a8a7dbc573e9db9b19d2fd1c98)
11b4bd023SJared McNeill /*-
21b4bd023SJared McNeill  * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
31b4bd023SJared McNeill  * All rights reserved.
41b4bd023SJared McNeill  *
51b4bd023SJared McNeill  * Redistribution and use in source and binary forms, with or without
61b4bd023SJared McNeill  * modification, are permitted provided that the following conditions
71b4bd023SJared McNeill  * are met:
81b4bd023SJared McNeill  * 1. Redistributions of source code must retain the above copyright
91b4bd023SJared McNeill  *    notice, this list of conditions and the following disclaimer.
101b4bd023SJared McNeill  * 2. Redistributions in binary form must reproduce the above copyright
111b4bd023SJared McNeill  *    notice, this list of conditions and the following disclaimer in the
121b4bd023SJared McNeill  *    documentation and/or other materials provided with the distribution.
131b4bd023SJared McNeill  *
141b4bd023SJared McNeill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
151b4bd023SJared McNeill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
161b4bd023SJared McNeill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
171b4bd023SJared McNeill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
181b4bd023SJared McNeill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
191b4bd023SJared McNeill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
201b4bd023SJared McNeill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
211b4bd023SJared McNeill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
221b4bd023SJared McNeill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231b4bd023SJared McNeill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241b4bd023SJared McNeill  * SUCH DAMAGE.
251b4bd023SJared McNeill  *
261b4bd023SJared McNeill  * $FreeBSD$
271b4bd023SJared McNeill  */
281b4bd023SJared McNeill 
291b4bd023SJared McNeill /*
301b4bd023SJared McNeill  * Allwinner USB PHY
311b4bd023SJared McNeill  */
321b4bd023SJared McNeill 
331b4bd023SJared McNeill #include <sys/cdefs.h>
341b4bd023SJared McNeill __FBSDID("$FreeBSD$");
351b4bd023SJared McNeill 
361b4bd023SJared McNeill #include <sys/param.h>
371b4bd023SJared McNeill #include <sys/systm.h>
381b4bd023SJared McNeill #include <sys/bus.h>
391b4bd023SJared McNeill #include <sys/rman.h>
401b4bd023SJared McNeill #include <sys/kernel.h>
411b4bd023SJared McNeill #include <sys/module.h>
421b4bd023SJared McNeill #include <sys/gpio.h>
4363dc81d8SJared McNeill #include <machine/bus.h>
441b4bd023SJared McNeill 
451b4bd023SJared McNeill #include <dev/ofw/ofw_bus.h>
461b4bd023SJared McNeill #include <dev/ofw/ofw_bus_subr.h>
47627c360fSJared McNeill #include <dev/gpio/gpiobusvar.h>
481b4bd023SJared McNeill 
496a05f063SJared McNeill #include <dev/extres/clk/clk.h>
506a05f063SJared McNeill #include <dev/extres/hwreset/hwreset.h>
516a05f063SJared McNeill #include <dev/extres/regulator/regulator.h>
52*84ebe16aSEmmanuel Vadot #include <dev/extres/phy/phy_usb.h>
531b4bd023SJared McNeill 
54f8759facSMichal Meloun #include "phynode_if.h"
55627c360fSJared McNeill 
5663dc81d8SJared McNeill enum awusbphy_type {
5763dc81d8SJared McNeill 	AWUSBPHY_TYPE_A10 = 1,
5863dc81d8SJared McNeill 	AWUSBPHY_TYPE_A13,
5963dc81d8SJared McNeill 	AWUSBPHY_TYPE_A20,
6063dc81d8SJared McNeill 	AWUSBPHY_TYPE_A31,
6163dc81d8SJared McNeill 	AWUSBPHY_TYPE_H3,
625b48129eSKyle Evans 	AWUSBPHY_TYPE_A64,
635b48129eSKyle Evans 	AWUSBPHY_TYPE_A83T
6463dc81d8SJared McNeill };
651b4bd023SJared McNeill 
6636dcd6a4SEmmanuel Vadot struct aw_usbphy_conf {
6736dcd6a4SEmmanuel Vadot 	int			num_phys;
6836dcd6a4SEmmanuel Vadot 	enum awusbphy_type	phy_type;
6936dcd6a4SEmmanuel Vadot 	bool			pmu_unk1;
7036dcd6a4SEmmanuel Vadot 	bool			phy0_route;
7136dcd6a4SEmmanuel Vadot };
7236dcd6a4SEmmanuel Vadot 
7336dcd6a4SEmmanuel Vadot static const struct aw_usbphy_conf a10_usbphy_conf = {
7436dcd6a4SEmmanuel Vadot 	.num_phys = 3,
7536dcd6a4SEmmanuel Vadot 	.phy_type = AWUSBPHY_TYPE_A10,
7636dcd6a4SEmmanuel Vadot 	.pmu_unk1 = false,
7736dcd6a4SEmmanuel Vadot 	.phy0_route = false,
7836dcd6a4SEmmanuel Vadot };
7936dcd6a4SEmmanuel Vadot 
8036dcd6a4SEmmanuel Vadot static const struct aw_usbphy_conf a13_usbphy_conf = {
8136dcd6a4SEmmanuel Vadot 	.num_phys = 2,
8236dcd6a4SEmmanuel Vadot 	.phy_type = AWUSBPHY_TYPE_A13,
8336dcd6a4SEmmanuel Vadot 	.pmu_unk1 = false,
8436dcd6a4SEmmanuel Vadot 	.phy0_route = false,
8536dcd6a4SEmmanuel Vadot };
8636dcd6a4SEmmanuel Vadot 
8736dcd6a4SEmmanuel Vadot static const struct aw_usbphy_conf a20_usbphy_conf = {
8836dcd6a4SEmmanuel Vadot 	.num_phys = 3,
8936dcd6a4SEmmanuel Vadot 	.phy_type = AWUSBPHY_TYPE_A20,
9036dcd6a4SEmmanuel Vadot 	.pmu_unk1 = false,
9136dcd6a4SEmmanuel Vadot 	.phy0_route = false,
9236dcd6a4SEmmanuel Vadot };
9336dcd6a4SEmmanuel Vadot 
9436dcd6a4SEmmanuel Vadot static const struct aw_usbphy_conf a31_usbphy_conf = {
9536dcd6a4SEmmanuel Vadot 	.num_phys = 3,
9636dcd6a4SEmmanuel Vadot 	.phy_type = AWUSBPHY_TYPE_A31,
9736dcd6a4SEmmanuel Vadot 	.pmu_unk1 = false,
9836dcd6a4SEmmanuel Vadot 	.phy0_route = false,
9936dcd6a4SEmmanuel Vadot };
10036dcd6a4SEmmanuel Vadot 
10136dcd6a4SEmmanuel Vadot static const struct aw_usbphy_conf h3_usbphy_conf = {
10236dcd6a4SEmmanuel Vadot 	.num_phys = 4,
10336dcd6a4SEmmanuel Vadot 	.phy_type = AWUSBPHY_TYPE_H3,
10436dcd6a4SEmmanuel Vadot 	.pmu_unk1 = true,
10536dcd6a4SEmmanuel Vadot 	.phy0_route = false,
10636dcd6a4SEmmanuel Vadot };
10736dcd6a4SEmmanuel Vadot 
10836dcd6a4SEmmanuel Vadot static const struct aw_usbphy_conf a64_usbphy_conf = {
10936dcd6a4SEmmanuel Vadot 	.num_phys = 2,
11036dcd6a4SEmmanuel Vadot 	.phy_type = AWUSBPHY_TYPE_A64,
11136dcd6a4SEmmanuel Vadot 	.pmu_unk1 = true,
11236dcd6a4SEmmanuel Vadot 	.phy0_route = true,
11336dcd6a4SEmmanuel Vadot };
11436dcd6a4SEmmanuel Vadot 
1155b48129eSKyle Evans static const struct aw_usbphy_conf a83t_usbphy_conf = {
1165b48129eSKyle Evans 	.num_phys = 3,
1175b48129eSKyle Evans 	.phy_type = AWUSBPHY_TYPE_A83T,
1185b48129eSKyle Evans 	.pmu_unk1 = false,
1195b48129eSKyle Evans 	.phy0_route = false,
1205b48129eSKyle Evans };
1215b48129eSKyle Evans 
1221b4bd023SJared McNeill static struct ofw_compat_data compat_data[] = {
12336dcd6a4SEmmanuel Vadot 	{ "allwinner,sun4i-a10-usb-phy",	(uintptr_t)&a10_usbphy_conf },
12436dcd6a4SEmmanuel Vadot 	{ "allwinner,sun5i-a13-usb-phy",	(uintptr_t)&a13_usbphy_conf },
12536dcd6a4SEmmanuel Vadot 	{ "allwinner,sun6i-a31-usb-phy",	(uintptr_t)&a31_usbphy_conf },
12636dcd6a4SEmmanuel Vadot 	{ "allwinner,sun7i-a20-usb-phy",	(uintptr_t)&a20_usbphy_conf },
12736dcd6a4SEmmanuel Vadot 	{ "allwinner,sun8i-h3-usb-phy",		(uintptr_t)&h3_usbphy_conf },
12836dcd6a4SEmmanuel Vadot 	{ "allwinner,sun50i-a64-usb-phy",	(uintptr_t)&a64_usbphy_conf },
1295b48129eSKyle Evans 	{ "allwinner,sun8i-a83t-usb-phy",	(uintptr_t)&a83t_usbphy_conf },
1301b4bd023SJared McNeill 	{ NULL,					0 }
1311b4bd023SJared McNeill };
1321b4bd023SJared McNeill 
133627c360fSJared McNeill struct awusbphy_softc {
13436dcd6a4SEmmanuel Vadot 	struct resource *	phy_ctrl;
13536dcd6a4SEmmanuel Vadot 	struct resource **	pmu;
13636dcd6a4SEmmanuel Vadot 	regulator_t *		reg;
137627c360fSJared McNeill 	gpio_pin_t		id_det_pin;
138627c360fSJared McNeill 	int			id_det_valid;
139627c360fSJared McNeill 	gpio_pin_t		vbus_det_pin;
140627c360fSJared McNeill 	int			vbus_det_valid;
14136dcd6a4SEmmanuel Vadot 	struct aw_usbphy_conf	*phy_conf;
142*84ebe16aSEmmanuel Vadot 	int			mode;
143627c360fSJared McNeill };
144627c360fSJared McNeill 
145f8759facSMichal Meloun  /* Phy class and methods. */
146f8759facSMichal Meloun static int awusbphy_phy_enable(struct phynode *phy, bool enable);
147*84ebe16aSEmmanuel Vadot static int awusbphy_get_mode(struct phynode *phy, int *mode);
148*84ebe16aSEmmanuel Vadot static int awusbphy_set_mode(struct phynode *phy, int mode);
149*84ebe16aSEmmanuel Vadot static phynode_usb_method_t awusbphy_phynode_methods[] = {
150f8759facSMichal Meloun 	PHYNODEMETHOD(phynode_enable, awusbphy_phy_enable),
151*84ebe16aSEmmanuel Vadot 	PHYNODEMETHOD(phynode_usb_get_mode, awusbphy_get_mode),
152*84ebe16aSEmmanuel Vadot 	PHYNODEMETHOD(phynode_usb_set_mode, awusbphy_set_mode),
153f8759facSMichal Meloun 
154f8759facSMichal Meloun 	PHYNODEMETHOD_END
155f8759facSMichal Meloun };
156f8759facSMichal Meloun DEFINE_CLASS_1(awusbphy_phynode, awusbphy_phynode_class, awusbphy_phynode_methods,
157*84ebe16aSEmmanuel Vadot   sizeof(struct phynode_usb_sc), phynode_usb_class);
158f8759facSMichal Meloun 
15936dcd6a4SEmmanuel Vadot #define	RD4(res, o)	bus_read_4(res, (o))
16036dcd6a4SEmmanuel Vadot #define	WR4(res, o, v)	bus_write_4(res, (o), (v))
16136dcd6a4SEmmanuel Vadot #define	CLR4(res, o, m)	WR4(res, o, RD4(res, o) & ~(m))
16236dcd6a4SEmmanuel Vadot #define	SET4(res, o, m)	WR4(res, o, RD4(res, o) | (m))
16363dc81d8SJared McNeill 
16463dc81d8SJared McNeill #define	OTG_PHY_CFG	0x20
16563dc81d8SJared McNeill #define	 OTG_PHY_ROUTE_OTG	(1 << 0)
16663dc81d8SJared McNeill #define	PMU_IRQ_ENABLE	0x00
16763dc81d8SJared McNeill #define	 PMU_AHB_INCR8		(1 << 10)
16863dc81d8SJared McNeill #define	 PMU_AHB_INCR4		(1 << 9)
16963dc81d8SJared McNeill #define	 PMU_AHB_INCRX_ALIGN	(1 << 8)
17063dc81d8SJared McNeill #define	 PMU_ULPI_BYPASS	(1 << 0)
17163dc81d8SJared McNeill #define	PMU_UNK_H3	0x10
17263dc81d8SJared McNeill #define	 PMU_UNK_H3_CLR		0x2
173*84ebe16aSEmmanuel Vadot #define	PHY_CSR		0x00
174*84ebe16aSEmmanuel Vadot #define	 ID_PULLUP_EN		(1 << 17)
175*84ebe16aSEmmanuel Vadot #define	 DPDM_PULLUP_EN		(1 << 16)
176*84ebe16aSEmmanuel Vadot #define	 FORCE_ID		(0x3 << 14)
177*84ebe16aSEmmanuel Vadot #define	 FORCE_ID_SHIFT		14
178*84ebe16aSEmmanuel Vadot #define	 FORCE_ID_LOW		2
179*84ebe16aSEmmanuel Vadot #define	 FORCE_VBUS_VALID	(0x3 << 12)
180*84ebe16aSEmmanuel Vadot #define	 FORCE_VBUS_VALID_SHIFT	12
181*84ebe16aSEmmanuel Vadot #define	 FORCE_VBUS_VALID_HIGH	3
182*84ebe16aSEmmanuel Vadot #define	 VBUS_CHANGE_DET	(1 << 6)
183*84ebe16aSEmmanuel Vadot #define	 ID_CHANGE_DET		(1 << 5)
184*84ebe16aSEmmanuel Vadot #define	 DPDM_CHANGE_DET	(1 << 4)
18563dc81d8SJared McNeill 
18663dc81d8SJared McNeill static void
18763dc81d8SJared McNeill awusbphy_configure(device_t dev, int phyno)
18863dc81d8SJared McNeill {
18963dc81d8SJared McNeill 	struct awusbphy_softc *sc;
19063dc81d8SJared McNeill 
19163dc81d8SJared McNeill 	sc = device_get_softc(dev);
19263dc81d8SJared McNeill 
19336dcd6a4SEmmanuel Vadot 	if (sc->pmu[phyno] == NULL)
19463dc81d8SJared McNeill 		return;
19563dc81d8SJared McNeill 
19636dcd6a4SEmmanuel Vadot 	if (sc->phy_conf->pmu_unk1 == true)
1971eca1d26SEmmanuel Vadot 		CLR4(sc->pmu[phyno], PMU_UNK_H3, PMU_UNK_H3_CLR);
19863dc81d8SJared McNeill 
19936dcd6a4SEmmanuel Vadot 	SET4(sc->pmu[phyno], PMU_IRQ_ENABLE, PMU_ULPI_BYPASS |
20063dc81d8SJared McNeill 	    PMU_AHB_INCR8 | PMU_AHB_INCR4 | PMU_AHB_INCRX_ALIGN);
20163dc81d8SJared McNeill }
20263dc81d8SJared McNeill 
2031b4bd023SJared McNeill static int
2041b4bd023SJared McNeill awusbphy_init(device_t dev)
2051b4bd023SJared McNeill {
206627c360fSJared McNeill 	struct awusbphy_softc *sc;
207627c360fSJared McNeill 	phandle_t node;
2081b4bd023SJared McNeill 	char pname[20];
20936dcd6a4SEmmanuel Vadot 	int error, off, rid;
2106a05f063SJared McNeill 	regulator_t reg;
2116a05f063SJared McNeill 	hwreset_t rst;
2126a05f063SJared McNeill 	clk_t clk;
2131b4bd023SJared McNeill 
214627c360fSJared McNeill 	sc = device_get_softc(dev);
215627c360fSJared McNeill 	node = ofw_bus_get_node(dev);
216627c360fSJared McNeill 
21736dcd6a4SEmmanuel Vadot 	sc->phy_conf = (struct aw_usbphy_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
21836dcd6a4SEmmanuel Vadot 
21936dcd6a4SEmmanuel Vadot 	/* Get phy_ctrl region */
22036dcd6a4SEmmanuel Vadot 	if (ofw_bus_find_string_index(node, "reg-names", "phy_ctrl", &rid) != 0) {
22136dcd6a4SEmmanuel Vadot 		device_printf(dev, "Cannot locate phy control resource\n");
22236dcd6a4SEmmanuel Vadot 		return (ENXIO);
22336dcd6a4SEmmanuel Vadot 	}
22436dcd6a4SEmmanuel Vadot 	sc->phy_ctrl = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
22536dcd6a4SEmmanuel Vadot 	    RF_ACTIVE);
22636dcd6a4SEmmanuel Vadot 	if (sc->phy_ctrl == NULL) {
22736dcd6a4SEmmanuel Vadot 		device_printf(dev, "Cannot allocate resource\n");
22836dcd6a4SEmmanuel Vadot 		return (ENXIO);
22936dcd6a4SEmmanuel Vadot 	}
23063dc81d8SJared McNeill 
2316a05f063SJared McNeill 	/* Enable clocks */
232dac93553SMichal Meloun 	for (off = 0; clk_get_by_ofw_index(dev, 0, off, &clk) == 0; off++) {
2336a05f063SJared McNeill 		error = clk_enable(clk);
2346a05f063SJared McNeill 		if (error != 0) {
2356a05f063SJared McNeill 			device_printf(dev, "couldn't enable clock %s\n",
2366a05f063SJared McNeill 			    clk_get_name(clk));
2376a05f063SJared McNeill 			return (error);
2386a05f063SJared McNeill 		}
2396a05f063SJared McNeill 	}
2401b4bd023SJared McNeill 
2416a05f063SJared McNeill 	/* De-assert resets */
242dac93553SMichal Meloun 	for (off = 0; hwreset_get_by_ofw_idx(dev, 0, off, &rst) == 0; off++) {
2436a05f063SJared McNeill 		error = hwreset_deassert(rst);
2446a05f063SJared McNeill 		if (error != 0) {
2456a05f063SJared McNeill 			device_printf(dev, "couldn't de-assert reset %d\n",
2466a05f063SJared McNeill 			    off);
2476a05f063SJared McNeill 			return (error);
2486a05f063SJared McNeill 		}
2496a05f063SJared McNeill 	}
2506a05f063SJared McNeill 
251627c360fSJared McNeill 	/* Get GPIOs */
252627c360fSJared McNeill 	error = gpio_pin_get_by_ofw_property(dev, node, "usb0_id_det-gpios",
253627c360fSJared McNeill 	    &sc->id_det_pin);
254627c360fSJared McNeill 	if (error == 0)
255627c360fSJared McNeill 		sc->id_det_valid = 1;
256627c360fSJared McNeill 	error = gpio_pin_get_by_ofw_property(dev, node, "usb0_vbus_det-gpios",
257627c360fSJared McNeill 	    &sc->vbus_det_pin);
258627c360fSJared McNeill 	if (error == 0)
259627c360fSJared McNeill 		sc->vbus_det_valid = 1;
260627c360fSJared McNeill 
26136dcd6a4SEmmanuel Vadot 	sc->reg = malloc(sizeof(*(sc->reg)) * sc->phy_conf->num_phys, M_DEVBUF,
26236dcd6a4SEmmanuel Vadot 	    M_WAITOK | M_ZERO);
26336dcd6a4SEmmanuel Vadot 	sc->pmu = malloc(sizeof(*(sc->pmu)) * sc->phy_conf->num_phys, M_DEVBUF,
26436dcd6a4SEmmanuel Vadot 	    M_WAITOK | M_ZERO);
26536dcd6a4SEmmanuel Vadot 	/* Get regulators */
26636dcd6a4SEmmanuel Vadot 	for (off = 0; off < sc->phy_conf->num_phys; off++) {
26736dcd6a4SEmmanuel Vadot 		snprintf(pname, sizeof(pname), "usb%d_vbus-supply", off);
26836dcd6a4SEmmanuel Vadot 		if (regulator_get_by_ofw_property(dev, 0, pname, &reg) == 0)
26936dcd6a4SEmmanuel Vadot 			sc->reg[off] = reg;
27036dcd6a4SEmmanuel Vadot 
27136dcd6a4SEmmanuel Vadot 		snprintf(pname, sizeof(pname), "pmu%d", off);
27236dcd6a4SEmmanuel Vadot 		if (ofw_bus_find_string_index(node, "reg-names",
27336dcd6a4SEmmanuel Vadot 		    pname, &rid) != 0)
27436dcd6a4SEmmanuel Vadot 			continue;
27536dcd6a4SEmmanuel Vadot 
27636dcd6a4SEmmanuel Vadot 		sc->pmu[off] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
27736dcd6a4SEmmanuel Vadot 		    RF_ACTIVE);
27836dcd6a4SEmmanuel Vadot 		if (sc->pmu[off] == NULL) {
27936dcd6a4SEmmanuel Vadot 			device_printf(dev, "Cannot allocate resource\n");
28036dcd6a4SEmmanuel Vadot 			return (ENXIO);
28136dcd6a4SEmmanuel Vadot 		}
28236dcd6a4SEmmanuel Vadot 	}
28363dc81d8SJared McNeill 
284627c360fSJared McNeill 	return (0);
285627c360fSJared McNeill }
286627c360fSJared McNeill 
287627c360fSJared McNeill static int
288627c360fSJared McNeill awusbphy_vbus_detect(device_t dev, int *val)
289627c360fSJared McNeill {
290627c360fSJared McNeill 	struct awusbphy_softc *sc;
291627c360fSJared McNeill 	bool active;
292627c360fSJared McNeill 	int error;
293627c360fSJared McNeill 
294627c360fSJared McNeill 	sc = device_get_softc(dev);
295627c360fSJared McNeill 
296627c360fSJared McNeill 	if (sc->vbus_det_valid) {
297627c360fSJared McNeill 		error = gpio_pin_is_active(sc->vbus_det_pin, &active);
298de355beaSEmmanuel Vadot 		if (error != 0) {
299de355beaSEmmanuel Vadot 			device_printf(dev, "Cannot get status of id pin %d\n",
300de355beaSEmmanuel Vadot 			    error);
301627c360fSJared McNeill 			return (error);
302de355beaSEmmanuel Vadot 		}
303627c360fSJared McNeill 		*val = active;
304627c360fSJared McNeill 		return (0);
305627c360fSJared McNeill 	}
306627c360fSJared McNeill 
307*84ebe16aSEmmanuel Vadot 	*val = 0;
308627c360fSJared McNeill 	return (0);
309627c360fSJared McNeill }
310627c360fSJared McNeill 
311627c360fSJared McNeill static int
312f8759facSMichal Meloun awusbphy_phy_enable(struct phynode *phynode, bool enable)
313627c360fSJared McNeill {
314f8759facSMichal Meloun 	device_t dev;
315f8759facSMichal Meloun 	intptr_t phy;
316627c360fSJared McNeill 	struct awusbphy_softc *sc;
317627c360fSJared McNeill 	regulator_t reg;
318627c360fSJared McNeill 	int error, vbus_det;
319627c360fSJared McNeill 
320f8759facSMichal Meloun 	dev = phynode_get_device(phynode);
321f8759facSMichal Meloun 	phy = phynode_get_id(phynode);
322627c360fSJared McNeill 	sc = device_get_softc(dev);
323627c360fSJared McNeill 
32436dcd6a4SEmmanuel Vadot 	if (phy < 0 || phy >= sc->phy_conf->num_phys)
32536dcd6a4SEmmanuel Vadot 		return (ERANGE);
32636dcd6a4SEmmanuel Vadot 
32763dc81d8SJared McNeill 	/* Configure PHY */
32863dc81d8SJared McNeill 	awusbphy_configure(dev, phy);
32963dc81d8SJared McNeill 
330627c360fSJared McNeill 	/* Regulators are optional. If not found, return success. */
331627c360fSJared McNeill 	reg = sc->reg[phy];
332627c360fSJared McNeill 	if (reg == NULL)
333627c360fSJared McNeill 		return (0);
334627c360fSJared McNeill 
335627c360fSJared McNeill 	if (phy == 0) {
336*84ebe16aSEmmanuel Vadot 		/* If an external vbus is detected, do not enable phy 0 */
337627c360fSJared McNeill 		error = awusbphy_vbus_detect(dev, &vbus_det);
338de355beaSEmmanuel Vadot 		if (error)
339de355beaSEmmanuel Vadot 			goto out;
340de355beaSEmmanuel Vadot 
341*84ebe16aSEmmanuel Vadot 		if (vbus_det == 1) {
342*84ebe16aSEmmanuel Vadot 			if (bootverbose)
343*84ebe16aSEmmanuel Vadot 				device_printf(dev, "External VBUS detected, not enabling the regulator\n");
344*84ebe16aSEmmanuel Vadot 
345627c360fSJared McNeill 			return (0);
346*84ebe16aSEmmanuel Vadot 		}
347*84ebe16aSEmmanuel Vadot 	}
348*84ebe16aSEmmanuel Vadot 	if (enable) {
349*84ebe16aSEmmanuel Vadot 		/* Depending on the PHY we need to route OTG to OHCI/EHCI */
350627c360fSJared McNeill 		error = regulator_enable(reg);
351627c360fSJared McNeill 	} else
352627c360fSJared McNeill 		error = regulator_disable(reg);
353de355beaSEmmanuel Vadot 
354de355beaSEmmanuel Vadot out:
355627c360fSJared McNeill 	if (error != 0) {
3567dd62272SJared McNeill 		device_printf(dev,
3577dd62272SJared McNeill 		    "couldn't %s regulator for phy %jd\n",
3587dd62272SJared McNeill 		    enable ? "enable" : "disable", (intmax_t)phy);
359627c360fSJared McNeill 		return (error);
3606a05f063SJared McNeill 	}
3611b4bd023SJared McNeill 
3621b4bd023SJared McNeill 	return (0);
3631b4bd023SJared McNeill }
3641b4bd023SJared McNeill 
3651b4bd023SJared McNeill static int
366*84ebe16aSEmmanuel Vadot awusbphy_get_mode(struct phynode *phynode, int *mode)
367*84ebe16aSEmmanuel Vadot {
368*84ebe16aSEmmanuel Vadot 	struct awusbphy_softc *sc;
369*84ebe16aSEmmanuel Vadot 	device_t dev;
370*84ebe16aSEmmanuel Vadot 
371*84ebe16aSEmmanuel Vadot 	dev = phynode_get_device(phynode);
372*84ebe16aSEmmanuel Vadot 	sc = device_get_softc(dev);
373*84ebe16aSEmmanuel Vadot 
374*84ebe16aSEmmanuel Vadot 	*mode = sc->mode;
375*84ebe16aSEmmanuel Vadot 
376*84ebe16aSEmmanuel Vadot 	return (0);
377*84ebe16aSEmmanuel Vadot }
378*84ebe16aSEmmanuel Vadot 
379*84ebe16aSEmmanuel Vadot static int
380*84ebe16aSEmmanuel Vadot awusbphy_set_mode(struct phynode *phynode, int mode)
381*84ebe16aSEmmanuel Vadot {
382*84ebe16aSEmmanuel Vadot 	device_t dev;
383*84ebe16aSEmmanuel Vadot 	intptr_t phy;
384*84ebe16aSEmmanuel Vadot 	struct awusbphy_softc *sc;
385*84ebe16aSEmmanuel Vadot 	uint32_t val;
386*84ebe16aSEmmanuel Vadot 	int error, vbus_det;
387*84ebe16aSEmmanuel Vadot 
388*84ebe16aSEmmanuel Vadot 	dev = phynode_get_device(phynode);
389*84ebe16aSEmmanuel Vadot 	phy = phynode_get_id(phynode);
390*84ebe16aSEmmanuel Vadot 	sc = device_get_softc(dev);
391*84ebe16aSEmmanuel Vadot 
392*84ebe16aSEmmanuel Vadot 	if (phy != 0)
393*84ebe16aSEmmanuel Vadot 		return (EINVAL);
394*84ebe16aSEmmanuel Vadot 
395*84ebe16aSEmmanuel Vadot 	switch (mode) {
396*84ebe16aSEmmanuel Vadot 	case PHY_USB_MODE_HOST:
397*84ebe16aSEmmanuel Vadot 		val = bus_read_4(sc->phy_ctrl, PHY_CSR);
398*84ebe16aSEmmanuel Vadot 		val &= ~(VBUS_CHANGE_DET | ID_CHANGE_DET | DPDM_CHANGE_DET);
399*84ebe16aSEmmanuel Vadot 		val |= (ID_PULLUP_EN | DPDM_PULLUP_EN);
400*84ebe16aSEmmanuel Vadot 		val &= ~FORCE_ID;
401*84ebe16aSEmmanuel Vadot 		val |= (FORCE_ID_LOW << FORCE_ID_SHIFT);
402*84ebe16aSEmmanuel Vadot 		val &= ~FORCE_VBUS_VALID;
403*84ebe16aSEmmanuel Vadot 		val |= (FORCE_VBUS_VALID_HIGH << FORCE_VBUS_VALID_SHIFT);
404*84ebe16aSEmmanuel Vadot 		bus_write_4(sc->phy_ctrl, PHY_CSR, val);
405*84ebe16aSEmmanuel Vadot 		if (sc->phy_conf->phy0_route == true) {
406*84ebe16aSEmmanuel Vadot 			error = awusbphy_vbus_detect(dev, &vbus_det);
407*84ebe16aSEmmanuel Vadot 			if (error)
408*84ebe16aSEmmanuel Vadot 				goto out;
409*84ebe16aSEmmanuel Vadot 			if (vbus_det == 0)
410*84ebe16aSEmmanuel Vadot 				CLR4(sc->phy_ctrl, OTG_PHY_CFG,
411*84ebe16aSEmmanuel Vadot 				  OTG_PHY_ROUTE_OTG);
412*84ebe16aSEmmanuel Vadot 			else
413*84ebe16aSEmmanuel Vadot 				SET4(sc->phy_ctrl, OTG_PHY_CFG,
414*84ebe16aSEmmanuel Vadot 				  OTG_PHY_ROUTE_OTG);
415*84ebe16aSEmmanuel Vadot 		}
416*84ebe16aSEmmanuel Vadot 		break;
417*84ebe16aSEmmanuel Vadot 	case PHY_USB_MODE_OTG:
418*84ebe16aSEmmanuel Vadot 		/* TODO */
419*84ebe16aSEmmanuel Vadot 		break;
420*84ebe16aSEmmanuel Vadot 	}
421*84ebe16aSEmmanuel Vadot 
422*84ebe16aSEmmanuel Vadot 	sc->mode = mode;
423*84ebe16aSEmmanuel Vadot 
424*84ebe16aSEmmanuel Vadot 
425*84ebe16aSEmmanuel Vadot out:
426*84ebe16aSEmmanuel Vadot 	return (0);
427*84ebe16aSEmmanuel Vadot }
428*84ebe16aSEmmanuel Vadot 
429*84ebe16aSEmmanuel Vadot static int
4301b4bd023SJared McNeill awusbphy_probe(device_t dev)
4311b4bd023SJared McNeill {
4321b4bd023SJared McNeill 	if (!ofw_bus_status_okay(dev))
4331b4bd023SJared McNeill 		return (ENXIO);
4341b4bd023SJared McNeill 
4351b4bd023SJared McNeill 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
4361b4bd023SJared McNeill 		return (ENXIO);
4371b4bd023SJared McNeill 
4381b4bd023SJared McNeill 	device_set_desc(dev, "Allwinner USB PHY");
4391b4bd023SJared McNeill 	return (BUS_PROBE_DEFAULT);
4401b4bd023SJared McNeill }
4411b4bd023SJared McNeill 
4421b4bd023SJared McNeill static int
4431b4bd023SJared McNeill awusbphy_attach(device_t dev)
4441b4bd023SJared McNeill {
4451b4bd023SJared McNeill 	int error;
446f8759facSMichal Meloun 	struct phynode *phynode;
447f8759facSMichal Meloun 	struct phynode_init_def phy_init;
448f8759facSMichal Meloun 	struct awusbphy_softc *sc;
449f8759facSMichal Meloun 	int i;
4501b4bd023SJared McNeill 
451f8759facSMichal Meloun 	sc = device_get_softc(dev);
4521b4bd023SJared McNeill 	error = awusbphy_init(dev);
453627c360fSJared McNeill 	if (error) {
4541b4bd023SJared McNeill 		device_printf(dev, "failed to initialize USB PHY, error %d\n",
4551b4bd023SJared McNeill 		    error);
456627c360fSJared McNeill 		return (error);
457627c360fSJared McNeill 	}
458627c360fSJared McNeill 
459f8759facSMichal Meloun 	/* Create and register phys. */
460f8759facSMichal Meloun 	for (i = 0; i < sc->phy_conf->num_phys; i++) {
461f8759facSMichal Meloun 		bzero(&phy_init, sizeof(phy_init));
462f8759facSMichal Meloun 		phy_init.id = i;
463f8759facSMichal Meloun 		phy_init.ofw_node = ofw_bus_get_node(dev);
464f8759facSMichal Meloun 		phynode = phynode_create(dev, &awusbphy_phynode_class,
465f8759facSMichal Meloun 		    &phy_init);
466f8759facSMichal Meloun 		if (phynode == NULL) {
467f8759facSMichal Meloun 			device_printf(dev, "failed to create USB PHY\n");
468f8759facSMichal Meloun 			return (ENXIO);
469f8759facSMichal Meloun 		}
470f8759facSMichal Meloun 		if (phynode_register(phynode) == NULL) {
471f8759facSMichal Meloun 			device_printf(dev, "failed to create USB PHY\n");
472f8759facSMichal Meloun 			return (ENXIO);
473f8759facSMichal Meloun 		}
474f8759facSMichal Meloun 	}
4751b4bd023SJared McNeill 
4761b4bd023SJared McNeill 	return (error);
4771b4bd023SJared McNeill }
4781b4bd023SJared McNeill 
4791b4bd023SJared McNeill static device_method_t awusbphy_methods[] = {
4801b4bd023SJared McNeill 	/* Device interface */
4811b4bd023SJared McNeill 	DEVMETHOD(device_probe,		awusbphy_probe),
4821b4bd023SJared McNeill 	DEVMETHOD(device_attach,	awusbphy_attach),
4831b4bd023SJared McNeill 
4841b4bd023SJared McNeill 	DEVMETHOD_END
4851b4bd023SJared McNeill };
4861b4bd023SJared McNeill 
4871b4bd023SJared McNeill static driver_t awusbphy_driver = {
4881b4bd023SJared McNeill 	"awusbphy",
4891b4bd023SJared McNeill 	awusbphy_methods,
490627c360fSJared McNeill 	sizeof(struct awusbphy_softc)
4911b4bd023SJared McNeill };
4921b4bd023SJared McNeill 
4931b4bd023SJared McNeill static devclass_t awusbphy_devclass;
4949f3b3133SKyle Evans /* aw_usbphy needs to come up after regulators/gpio/etc, but before ehci/ohci */
495627c360fSJared McNeill EARLY_DRIVER_MODULE(awusbphy, simplebus, awusbphy_driver, awusbphy_devclass,
4969f3b3133SKyle Evans     0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
4971b4bd023SJared McNeill MODULE_VERSION(awusbphy, 1);
498