xref: /freebsd/sys/arm/allwinner/aw_usbphy.c (revision 9eb30ef4b7a0ca1ef7bcc871b6391d98b00c259f)
11b4bd023SJared McNeill /*-
21b4bd023SJared McNeill  * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
31b4bd023SJared McNeill  *
41b4bd023SJared McNeill  * Redistribution and use in source and binary forms, with or without
51b4bd023SJared McNeill  * modification, are permitted provided that the following conditions
61b4bd023SJared McNeill  * are met:
71b4bd023SJared McNeill  * 1. Redistributions of source code must retain the above copyright
81b4bd023SJared McNeill  *    notice, this list of conditions and the following disclaimer.
91b4bd023SJared McNeill  * 2. Redistributions in binary form must reproduce the above copyright
101b4bd023SJared McNeill  *    notice, this list of conditions and the following disclaimer in the
111b4bd023SJared McNeill  *    documentation and/or other materials provided with the distribution.
121b4bd023SJared McNeill  *
131b4bd023SJared McNeill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
141b4bd023SJared McNeill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
151b4bd023SJared McNeill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
161b4bd023SJared McNeill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
171b4bd023SJared McNeill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
181b4bd023SJared McNeill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
191b4bd023SJared McNeill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
201b4bd023SJared McNeill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
211b4bd023SJared McNeill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
221b4bd023SJared McNeill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
231b4bd023SJared McNeill  * SUCH DAMAGE.
241b4bd023SJared McNeill  */
251b4bd023SJared McNeill 
261b4bd023SJared McNeill /*
271b4bd023SJared McNeill  * Allwinner USB PHY
281b4bd023SJared McNeill  */
291b4bd023SJared McNeill 
301b4bd023SJared McNeill #include <sys/param.h>
311b4bd023SJared McNeill #include <sys/systm.h>
321b4bd023SJared McNeill #include <sys/bus.h>
331b4bd023SJared McNeill #include <sys/rman.h>
341b4bd023SJared McNeill #include <sys/kernel.h>
351b4bd023SJared McNeill #include <sys/module.h>
361b4bd023SJared McNeill #include <sys/gpio.h>
3763dc81d8SJared McNeill #include <machine/bus.h>
381b4bd023SJared McNeill 
391b4bd023SJared McNeill #include <dev/ofw/ofw_bus.h>
401b4bd023SJared McNeill #include <dev/ofw/ofw_bus_subr.h>
41627c360fSJared McNeill #include <dev/gpio/gpiobusvar.h>
421b4bd023SJared McNeill 
43be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
441f469a9fSEmmanuel Vadot #include <dev/hwreset/hwreset.h>
45b2f0caf1SEmmanuel Vadot #include <dev/regulator/regulator.h>
46950a6087SEmmanuel Vadot #include <dev/phy/phy_usb.h>
471b4bd023SJared McNeill 
48f8759facSMichal Meloun #include "phynode_if.h"
49627c360fSJared McNeill 
5063dc81d8SJared McNeill enum awusbphy_type {
5163dc81d8SJared McNeill 	AWUSBPHY_TYPE_A10 = 1,
5263dc81d8SJared McNeill 	AWUSBPHY_TYPE_A13,
5363dc81d8SJared McNeill 	AWUSBPHY_TYPE_A20,
5463dc81d8SJared McNeill 	AWUSBPHY_TYPE_A31,
5563dc81d8SJared McNeill 	AWUSBPHY_TYPE_H3,
565b48129eSKyle Evans 	AWUSBPHY_TYPE_A64,
57126700faSEmmanuel Vadot 	AWUSBPHY_TYPE_A83T,
58126700faSEmmanuel Vadot 	AWUSBPHY_TYPE_H6,
59*9eb30ef4SMitchell Horne 	AWUSBPHY_TYPE_D1,
6063dc81d8SJared McNeill };
611b4bd023SJared McNeill 
6236dcd6a4SEmmanuel Vadot struct aw_usbphy_conf {
6336dcd6a4SEmmanuel Vadot 	int			num_phys;
6436dcd6a4SEmmanuel Vadot 	enum awusbphy_type	phy_type;
6536dcd6a4SEmmanuel Vadot 	bool			pmu_unk1;
6636dcd6a4SEmmanuel Vadot 	bool			phy0_route;
6736dcd6a4SEmmanuel Vadot };
6836dcd6a4SEmmanuel Vadot 
6936dcd6a4SEmmanuel Vadot static const struct aw_usbphy_conf a10_usbphy_conf = {
7036dcd6a4SEmmanuel Vadot 	.num_phys = 3,
7136dcd6a4SEmmanuel Vadot 	.phy_type = AWUSBPHY_TYPE_A10,
7236dcd6a4SEmmanuel Vadot 	.pmu_unk1 = false,
7336dcd6a4SEmmanuel Vadot 	.phy0_route = false,
7436dcd6a4SEmmanuel Vadot };
7536dcd6a4SEmmanuel Vadot 
7636dcd6a4SEmmanuel Vadot static const struct aw_usbphy_conf a13_usbphy_conf = {
7736dcd6a4SEmmanuel Vadot 	.num_phys = 2,
7836dcd6a4SEmmanuel Vadot 	.phy_type = AWUSBPHY_TYPE_A13,
7936dcd6a4SEmmanuel Vadot 	.pmu_unk1 = false,
8036dcd6a4SEmmanuel Vadot 	.phy0_route = false,
8136dcd6a4SEmmanuel Vadot };
8236dcd6a4SEmmanuel Vadot 
8336dcd6a4SEmmanuel Vadot static const struct aw_usbphy_conf a20_usbphy_conf = {
8436dcd6a4SEmmanuel Vadot 	.num_phys = 3,
8536dcd6a4SEmmanuel Vadot 	.phy_type = AWUSBPHY_TYPE_A20,
8636dcd6a4SEmmanuel Vadot 	.pmu_unk1 = false,
8736dcd6a4SEmmanuel Vadot 	.phy0_route = false,
8836dcd6a4SEmmanuel Vadot };
8936dcd6a4SEmmanuel Vadot 
9036dcd6a4SEmmanuel Vadot static const struct aw_usbphy_conf a31_usbphy_conf = {
9136dcd6a4SEmmanuel Vadot 	.num_phys = 3,
9236dcd6a4SEmmanuel Vadot 	.phy_type = AWUSBPHY_TYPE_A31,
9336dcd6a4SEmmanuel Vadot 	.pmu_unk1 = false,
9436dcd6a4SEmmanuel Vadot 	.phy0_route = false,
9536dcd6a4SEmmanuel Vadot };
9636dcd6a4SEmmanuel Vadot 
9736dcd6a4SEmmanuel Vadot static const struct aw_usbphy_conf h3_usbphy_conf = {
9836dcd6a4SEmmanuel Vadot 	.num_phys = 4,
9936dcd6a4SEmmanuel Vadot 	.phy_type = AWUSBPHY_TYPE_H3,
10036dcd6a4SEmmanuel Vadot 	.pmu_unk1 = true,
101aea49d9fSAndriy Gapon 	.phy0_route = true,
10236dcd6a4SEmmanuel Vadot };
10336dcd6a4SEmmanuel Vadot 
10436dcd6a4SEmmanuel Vadot static const struct aw_usbphy_conf a64_usbphy_conf = {
10536dcd6a4SEmmanuel Vadot 	.num_phys = 2,
10636dcd6a4SEmmanuel Vadot 	.phy_type = AWUSBPHY_TYPE_A64,
10736dcd6a4SEmmanuel Vadot 	.pmu_unk1 = true,
10836dcd6a4SEmmanuel Vadot 	.phy0_route = true,
10936dcd6a4SEmmanuel Vadot };
11036dcd6a4SEmmanuel Vadot 
1115b48129eSKyle Evans static const struct aw_usbphy_conf a83t_usbphy_conf = {
1125b48129eSKyle Evans 	.num_phys = 3,
1135b48129eSKyle Evans 	.phy_type = AWUSBPHY_TYPE_A83T,
1145b48129eSKyle Evans 	.pmu_unk1 = false,
1155b48129eSKyle Evans 	.phy0_route = false,
1165b48129eSKyle Evans };
1175b48129eSKyle Evans 
118126700faSEmmanuel Vadot static const struct aw_usbphy_conf h6_usbphy_conf = {
119126700faSEmmanuel Vadot 	.num_phys = 4,
120126700faSEmmanuel Vadot 	.phy_type = AWUSBPHY_TYPE_H6,
121126700faSEmmanuel Vadot 	.pmu_unk1 = false,
122126700faSEmmanuel Vadot 	.phy0_route = true,
123126700faSEmmanuel Vadot };
124126700faSEmmanuel Vadot 
125*9eb30ef4SMitchell Horne static const struct aw_usbphy_conf d1_usbphy_conf = {
126*9eb30ef4SMitchell Horne 	.num_phys = 2,
127*9eb30ef4SMitchell Horne 	.phy_type = AWUSBPHY_TYPE_D1,
128*9eb30ef4SMitchell Horne 	.pmu_unk1 = true,
129*9eb30ef4SMitchell Horne 	.phy0_route = true,
130*9eb30ef4SMitchell Horne };
131*9eb30ef4SMitchell Horne 
1321b4bd023SJared McNeill static struct ofw_compat_data compat_data[] = {
13336dcd6a4SEmmanuel Vadot 	{ "allwinner,sun4i-a10-usb-phy",	(uintptr_t)&a10_usbphy_conf },
13436dcd6a4SEmmanuel Vadot 	{ "allwinner,sun5i-a13-usb-phy",	(uintptr_t)&a13_usbphy_conf },
13536dcd6a4SEmmanuel Vadot 	{ "allwinner,sun6i-a31-usb-phy",	(uintptr_t)&a31_usbphy_conf },
13636dcd6a4SEmmanuel Vadot 	{ "allwinner,sun7i-a20-usb-phy",	(uintptr_t)&a20_usbphy_conf },
13736dcd6a4SEmmanuel Vadot 	{ "allwinner,sun8i-h3-usb-phy",		(uintptr_t)&h3_usbphy_conf },
13836dcd6a4SEmmanuel Vadot 	{ "allwinner,sun50i-a64-usb-phy",	(uintptr_t)&a64_usbphy_conf },
1395b48129eSKyle Evans 	{ "allwinner,sun8i-a83t-usb-phy",	(uintptr_t)&a83t_usbphy_conf },
140126700faSEmmanuel Vadot 	{ "allwinner,sun50i-h6-usb-phy",	(uintptr_t)&h6_usbphy_conf },
141*9eb30ef4SMitchell Horne 	{ "allwinner,sun20i-d1-usb-phy",	(uintptr_t)&d1_usbphy_conf },
1421b4bd023SJared McNeill 	{ NULL,					0 }
1431b4bd023SJared McNeill };
1441b4bd023SJared McNeill 
145627c360fSJared McNeill struct awusbphy_softc {
14636dcd6a4SEmmanuel Vadot 	struct resource *	phy_ctrl;
14736dcd6a4SEmmanuel Vadot 	struct resource **	pmu;
14836dcd6a4SEmmanuel Vadot 	regulator_t *		reg;
149627c360fSJared McNeill 	gpio_pin_t		id_det_pin;
150627c360fSJared McNeill 	int			id_det_valid;
151627c360fSJared McNeill 	gpio_pin_t		vbus_det_pin;
152627c360fSJared McNeill 	int			vbus_det_valid;
15336dcd6a4SEmmanuel Vadot 	struct aw_usbphy_conf	*phy_conf;
15484ebe16aSEmmanuel Vadot 	int			mode;
155627c360fSJared McNeill };
156627c360fSJared McNeill 
157f8759facSMichal Meloun  /* Phy class and methods. */
158f8759facSMichal Meloun static int awusbphy_phy_enable(struct phynode *phy, bool enable);
15984ebe16aSEmmanuel Vadot static int awusbphy_get_mode(struct phynode *phy, int *mode);
16084ebe16aSEmmanuel Vadot static int awusbphy_set_mode(struct phynode *phy, int mode);
16184ebe16aSEmmanuel Vadot static phynode_usb_method_t awusbphy_phynode_methods[] = {
162f8759facSMichal Meloun 	PHYNODEMETHOD(phynode_enable, awusbphy_phy_enable),
16384ebe16aSEmmanuel Vadot 	PHYNODEMETHOD(phynode_usb_get_mode, awusbphy_get_mode),
16484ebe16aSEmmanuel Vadot 	PHYNODEMETHOD(phynode_usb_set_mode, awusbphy_set_mode),
165f8759facSMichal Meloun 
166f8759facSMichal Meloun 	PHYNODEMETHOD_END
167f8759facSMichal Meloun };
168f8759facSMichal Meloun DEFINE_CLASS_1(awusbphy_phynode, awusbphy_phynode_class, awusbphy_phynode_methods,
16984ebe16aSEmmanuel Vadot   sizeof(struct phynode_usb_sc), phynode_usb_class);
170f8759facSMichal Meloun 
17136dcd6a4SEmmanuel Vadot #define	RD4(res, o)	bus_read_4(res, (o))
17236dcd6a4SEmmanuel Vadot #define	WR4(res, o, v)	bus_write_4(res, (o), (v))
17336dcd6a4SEmmanuel Vadot #define	CLR4(res, o, m)	WR4(res, o, RD4(res, o) & ~(m))
17436dcd6a4SEmmanuel Vadot #define	SET4(res, o, m)	WR4(res, o, RD4(res, o) | (m))
17563dc81d8SJared McNeill 
176d9a65ba8SAndriy Gapon #define	PHY_CSR		0x00
177d9a65ba8SAndriy Gapon #define	 ID_PULLUP_EN		(1 << 17)
178d9a65ba8SAndriy Gapon #define	 DPDM_PULLUP_EN		(1 << 16)
179d9a65ba8SAndriy Gapon #define	 FORCE_ID		(0x3 << 14)
180d9a65ba8SAndriy Gapon #define	 FORCE_ID_SHIFT		14
181d9a65ba8SAndriy Gapon #define	 FORCE_ID_LOW		2
182aea49d9fSAndriy Gapon #define	 FORCE_ID_HIGH		3
183d9a65ba8SAndriy Gapon #define	 FORCE_VBUS_VALID	(0x3 << 12)
184d9a65ba8SAndriy Gapon #define	 FORCE_VBUS_VALID_SHIFT	12
185aea49d9fSAndriy Gapon #define	 FORCE_VBUS_VALID_LOW	2
186d9a65ba8SAndriy Gapon #define	 FORCE_VBUS_VALID_HIGH	3
187d9a65ba8SAndriy Gapon #define	 VBUS_CHANGE_DET	(1 << 6)
188d9a65ba8SAndriy Gapon #define	 ID_CHANGE_DET		(1 << 5)
189d9a65ba8SAndriy Gapon #define	 DPDM_CHANGE_DET	(1 << 4)
19063dc81d8SJared McNeill #define	OTG_PHY_CFG	0x20
19163dc81d8SJared McNeill #define	 OTG_PHY_ROUTE_OTG	(1 << 0)
19263dc81d8SJared McNeill #define	PMU_IRQ_ENABLE	0x00
19363dc81d8SJared McNeill #define	 PMU_AHB_INCR8		(1 << 10)
19463dc81d8SJared McNeill #define	 PMU_AHB_INCR4		(1 << 9)
19563dc81d8SJared McNeill #define	 PMU_AHB_INCRX_ALIGN	(1 << 8)
19663dc81d8SJared McNeill #define	 PMU_ULPI_BYPASS	(1 << 0)
19763dc81d8SJared McNeill #define	PMU_UNK_H3	0x10
19863dc81d8SJared McNeill #define	 PMU_UNK_H3_CLR		0x2
19963dc81d8SJared McNeill 
20063dc81d8SJared McNeill static void
awusbphy_configure(device_t dev,int phyno)20163dc81d8SJared McNeill awusbphy_configure(device_t dev, int phyno)
20263dc81d8SJared McNeill {
20363dc81d8SJared McNeill 	struct awusbphy_softc *sc;
20463dc81d8SJared McNeill 
20563dc81d8SJared McNeill 	sc = device_get_softc(dev);
20663dc81d8SJared McNeill 
20736dcd6a4SEmmanuel Vadot 	if (sc->pmu[phyno] == NULL)
20863dc81d8SJared McNeill 		return;
20963dc81d8SJared McNeill 
21036dcd6a4SEmmanuel Vadot 	if (sc->phy_conf->pmu_unk1 == true)
2111eca1d26SEmmanuel Vadot 		CLR4(sc->pmu[phyno], PMU_UNK_H3, PMU_UNK_H3_CLR);
21263dc81d8SJared McNeill 
21336dcd6a4SEmmanuel Vadot 	SET4(sc->pmu[phyno], PMU_IRQ_ENABLE, PMU_ULPI_BYPASS |
21463dc81d8SJared McNeill 	    PMU_AHB_INCR8 | PMU_AHB_INCR4 | PMU_AHB_INCRX_ALIGN);
21563dc81d8SJared McNeill }
21663dc81d8SJared McNeill 
2171b4bd023SJared McNeill static int
awusbphy_init(device_t dev)2181b4bd023SJared McNeill awusbphy_init(device_t dev)
2191b4bd023SJared McNeill {
220627c360fSJared McNeill 	struct awusbphy_softc *sc;
221627c360fSJared McNeill 	phandle_t node;
2221b4bd023SJared McNeill 	char pname[20];
223d9a65ba8SAndriy Gapon 	uint32_t val;
22436dcd6a4SEmmanuel Vadot 	int error, off, rid;
2256a05f063SJared McNeill 	regulator_t reg;
2266a05f063SJared McNeill 	hwreset_t rst;
2276a05f063SJared McNeill 	clk_t clk;
2281b4bd023SJared McNeill 
229627c360fSJared McNeill 	sc = device_get_softc(dev);
230627c360fSJared McNeill 	node = ofw_bus_get_node(dev);
231627c360fSJared McNeill 
23236dcd6a4SEmmanuel Vadot 	sc->phy_conf = (struct aw_usbphy_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
23336dcd6a4SEmmanuel Vadot 
23436dcd6a4SEmmanuel Vadot 	/* Get phy_ctrl region */
23536dcd6a4SEmmanuel Vadot 	if (ofw_bus_find_string_index(node, "reg-names", "phy_ctrl", &rid) != 0) {
23636dcd6a4SEmmanuel Vadot 		device_printf(dev, "Cannot locate phy control resource\n");
23736dcd6a4SEmmanuel Vadot 		return (ENXIO);
23836dcd6a4SEmmanuel Vadot 	}
23936dcd6a4SEmmanuel Vadot 	sc->phy_ctrl = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
24036dcd6a4SEmmanuel Vadot 	    RF_ACTIVE);
24136dcd6a4SEmmanuel Vadot 	if (sc->phy_ctrl == NULL) {
24236dcd6a4SEmmanuel Vadot 		device_printf(dev, "Cannot allocate resource\n");
24336dcd6a4SEmmanuel Vadot 		return (ENXIO);
24436dcd6a4SEmmanuel Vadot 	}
24563dc81d8SJared McNeill 
2466a05f063SJared McNeill 	/* Enable clocks */
247dac93553SMichal Meloun 	for (off = 0; clk_get_by_ofw_index(dev, 0, off, &clk) == 0; off++) {
2486a05f063SJared McNeill 		error = clk_enable(clk);
2496a05f063SJared McNeill 		if (error != 0) {
2506a05f063SJared McNeill 			device_printf(dev, "couldn't enable clock %s\n",
2516a05f063SJared McNeill 			    clk_get_name(clk));
2526a05f063SJared McNeill 			return (error);
2536a05f063SJared McNeill 		}
2546a05f063SJared McNeill 	}
2551b4bd023SJared McNeill 
2566a05f063SJared McNeill 	/* De-assert resets */
257dac93553SMichal Meloun 	for (off = 0; hwreset_get_by_ofw_idx(dev, 0, off, &rst) == 0; off++) {
2586a05f063SJared McNeill 		error = hwreset_deassert(rst);
2596a05f063SJared McNeill 		if (error != 0) {
2606a05f063SJared McNeill 			device_printf(dev, "couldn't de-assert reset %d\n",
2616a05f063SJared McNeill 			    off);
2626a05f063SJared McNeill 			return (error);
2636a05f063SJared McNeill 		}
2646a05f063SJared McNeill 	}
2656a05f063SJared McNeill 
266627c360fSJared McNeill 	/* Get GPIOs */
267627c360fSJared McNeill 	error = gpio_pin_get_by_ofw_property(dev, node, "usb0_id_det-gpios",
268627c360fSJared McNeill 	    &sc->id_det_pin);
269627c360fSJared McNeill 	if (error == 0)
270627c360fSJared McNeill 		sc->id_det_valid = 1;
271627c360fSJared McNeill 	error = gpio_pin_get_by_ofw_property(dev, node, "usb0_vbus_det-gpios",
272627c360fSJared McNeill 	    &sc->vbus_det_pin);
273627c360fSJared McNeill 	if (error == 0)
274627c360fSJared McNeill 		sc->vbus_det_valid = 1;
275627c360fSJared McNeill 
27636dcd6a4SEmmanuel Vadot 	sc->reg = malloc(sizeof(*(sc->reg)) * sc->phy_conf->num_phys, M_DEVBUF,
27736dcd6a4SEmmanuel Vadot 	    M_WAITOK | M_ZERO);
27836dcd6a4SEmmanuel Vadot 	sc->pmu = malloc(sizeof(*(sc->pmu)) * sc->phy_conf->num_phys, M_DEVBUF,
27936dcd6a4SEmmanuel Vadot 	    M_WAITOK | M_ZERO);
28036dcd6a4SEmmanuel Vadot 	/* Get regulators */
28136dcd6a4SEmmanuel Vadot 	for (off = 0; off < sc->phy_conf->num_phys; off++) {
28236dcd6a4SEmmanuel Vadot 		snprintf(pname, sizeof(pname), "usb%d_vbus-supply", off);
28336dcd6a4SEmmanuel Vadot 		if (regulator_get_by_ofw_property(dev, 0, pname, &reg) == 0)
28436dcd6a4SEmmanuel Vadot 			sc->reg[off] = reg;
28536dcd6a4SEmmanuel Vadot 
28636dcd6a4SEmmanuel Vadot 		snprintf(pname, sizeof(pname), "pmu%d", off);
28736dcd6a4SEmmanuel Vadot 		if (ofw_bus_find_string_index(node, "reg-names",
28836dcd6a4SEmmanuel Vadot 		    pname, &rid) != 0)
28936dcd6a4SEmmanuel Vadot 			continue;
29036dcd6a4SEmmanuel Vadot 
29136dcd6a4SEmmanuel Vadot 		sc->pmu[off] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
29236dcd6a4SEmmanuel Vadot 		    RF_ACTIVE);
29336dcd6a4SEmmanuel Vadot 		if (sc->pmu[off] == NULL) {
29436dcd6a4SEmmanuel Vadot 			device_printf(dev, "Cannot allocate resource\n");
29536dcd6a4SEmmanuel Vadot 			return (ENXIO);
29636dcd6a4SEmmanuel Vadot 		}
29736dcd6a4SEmmanuel Vadot 	}
29863dc81d8SJared McNeill 
299d9a65ba8SAndriy Gapon 	/* Enable OTG PHY for host mode */
300d9a65ba8SAndriy Gapon 	val = bus_read_4(sc->phy_ctrl, PHY_CSR);
301d9a65ba8SAndriy Gapon 	val &= ~(VBUS_CHANGE_DET | ID_CHANGE_DET | DPDM_CHANGE_DET);
302d9a65ba8SAndriy Gapon 	val |= (ID_PULLUP_EN | DPDM_PULLUP_EN);
303d9a65ba8SAndriy Gapon 	val &= ~FORCE_ID;
304d9a65ba8SAndriy Gapon 	val |= (FORCE_ID_LOW << FORCE_ID_SHIFT);
305d9a65ba8SAndriy Gapon 	val &= ~FORCE_VBUS_VALID;
306d9a65ba8SAndriy Gapon 	val |= (FORCE_VBUS_VALID_HIGH << FORCE_VBUS_VALID_SHIFT);
307d9a65ba8SAndriy Gapon 	bus_write_4(sc->phy_ctrl, PHY_CSR, val);
308d9a65ba8SAndriy Gapon 
309627c360fSJared McNeill 	return (0);
310627c360fSJared McNeill }
311627c360fSJared McNeill 
312627c360fSJared McNeill static int
awusbphy_vbus_detect(device_t dev,int * val)313627c360fSJared McNeill awusbphy_vbus_detect(device_t dev, int *val)
314627c360fSJared McNeill {
315627c360fSJared McNeill 	struct awusbphy_softc *sc;
316627c360fSJared McNeill 	bool active;
317627c360fSJared McNeill 	int error;
318627c360fSJared McNeill 
319627c360fSJared McNeill 	sc = device_get_softc(dev);
320627c360fSJared McNeill 
321627c360fSJared McNeill 	if (sc->vbus_det_valid) {
322627c360fSJared McNeill 		error = gpio_pin_is_active(sc->vbus_det_pin, &active);
323de355beaSEmmanuel Vadot 		if (error != 0) {
324de355beaSEmmanuel Vadot 			device_printf(dev, "Cannot get status of id pin %d\n",
325de355beaSEmmanuel Vadot 			    error);
326627c360fSJared McNeill 			return (error);
327de355beaSEmmanuel Vadot 		}
328627c360fSJared McNeill 		*val = active;
329627c360fSJared McNeill 		return (0);
330627c360fSJared McNeill 	}
331627c360fSJared McNeill 
332aea49d9fSAndriy Gapon 	/* TODO check vbus_power-supply. */
333aea49d9fSAndriy Gapon 
334aea49d9fSAndriy Gapon 	/*
335aea49d9fSAndriy Gapon 	 * If there is no way to detect, assume present.
336aea49d9fSAndriy Gapon 	 */
337aea49d9fSAndriy Gapon 	*val = 1;
338627c360fSJared McNeill 	return (0);
339627c360fSJared McNeill }
340627c360fSJared McNeill 
341627c360fSJared McNeill static int
awusbphy_phy_enable(struct phynode * phynode,bool enable)342f8759facSMichal Meloun awusbphy_phy_enable(struct phynode *phynode, bool enable)
343627c360fSJared McNeill {
344f8759facSMichal Meloun 	device_t dev;
345f8759facSMichal Meloun 	intptr_t phy;
346627c360fSJared McNeill 	struct awusbphy_softc *sc;
347627c360fSJared McNeill 	regulator_t reg;
348627c360fSJared McNeill 	int error, vbus_det;
349627c360fSJared McNeill 
350f8759facSMichal Meloun 	dev = phynode_get_device(phynode);
351f8759facSMichal Meloun 	phy = phynode_get_id(phynode);
352627c360fSJared McNeill 	sc = device_get_softc(dev);
353627c360fSJared McNeill 
35436dcd6a4SEmmanuel Vadot 	if (phy < 0 || phy >= sc->phy_conf->num_phys)
35536dcd6a4SEmmanuel Vadot 		return (ERANGE);
35636dcd6a4SEmmanuel Vadot 
35763dc81d8SJared McNeill 	/* Configure PHY */
35863dc81d8SJared McNeill 	awusbphy_configure(dev, phy);
35963dc81d8SJared McNeill 
360627c360fSJared McNeill 	/* Regulators are optional. If not found, return success. */
361627c360fSJared McNeill 	reg = sc->reg[phy];
362627c360fSJared McNeill 	if (reg == NULL)
363627c360fSJared McNeill 		return (0);
364627c360fSJared McNeill 
365627c360fSJared McNeill 	if (phy == 0) {
36684ebe16aSEmmanuel Vadot 		/* If an external vbus is detected, do not enable phy 0 */
367627c360fSJared McNeill 		error = awusbphy_vbus_detect(dev, &vbus_det);
368de355beaSEmmanuel Vadot 		if (error)
369de355beaSEmmanuel Vadot 			goto out;
370de355beaSEmmanuel Vadot 
371aea49d9fSAndriy Gapon 		/* TODO check vbus_power-supply as well. */
372aea49d9fSAndriy Gapon 		if (sc->vbus_det_valid && vbus_det == 1) {
37384ebe16aSEmmanuel Vadot 			if (bootverbose)
374aea49d9fSAndriy Gapon 				device_printf(dev, "External VBUS detected, "
375aea49d9fSAndriy Gapon 				    "not enabling the regulator\n");
376627c360fSJared McNeill 			return (0);
37784ebe16aSEmmanuel Vadot 		}
37884ebe16aSEmmanuel Vadot 	}
37984ebe16aSEmmanuel Vadot 	if (enable) {
38084ebe16aSEmmanuel Vadot 		/* Depending on the PHY we need to route OTG to OHCI/EHCI */
381627c360fSJared McNeill 		error = regulator_enable(reg);
382627c360fSJared McNeill 	} else
383627c360fSJared McNeill 		error = regulator_disable(reg);
384de355beaSEmmanuel Vadot 
385de355beaSEmmanuel Vadot out:
386627c360fSJared McNeill 	if (error != 0) {
3877dd62272SJared McNeill 		device_printf(dev,
3887dd62272SJared McNeill 		    "couldn't %s regulator for phy %jd\n",
3897dd62272SJared McNeill 		    enable ? "enable" : "disable", (intmax_t)phy);
390627c360fSJared McNeill 		return (error);
3916a05f063SJared McNeill 	}
3921b4bd023SJared McNeill 
3931b4bd023SJared McNeill 	return (0);
3941b4bd023SJared McNeill }
3951b4bd023SJared McNeill 
3961b4bd023SJared McNeill static int
awusbphy_get_mode(struct phynode * phynode,int * mode)39784ebe16aSEmmanuel Vadot awusbphy_get_mode(struct phynode *phynode, int *mode)
39884ebe16aSEmmanuel Vadot {
39984ebe16aSEmmanuel Vadot 	struct awusbphy_softc *sc;
40084ebe16aSEmmanuel Vadot 	device_t dev;
40184ebe16aSEmmanuel Vadot 
40284ebe16aSEmmanuel Vadot 	dev = phynode_get_device(phynode);
40384ebe16aSEmmanuel Vadot 	sc = device_get_softc(dev);
40484ebe16aSEmmanuel Vadot 
40584ebe16aSEmmanuel Vadot 	*mode = sc->mode;
40684ebe16aSEmmanuel Vadot 
40784ebe16aSEmmanuel Vadot 	return (0);
40884ebe16aSEmmanuel Vadot }
40984ebe16aSEmmanuel Vadot 
41084ebe16aSEmmanuel Vadot static int
awusbphy_set_mode(struct phynode * phynode,int mode)41184ebe16aSEmmanuel Vadot awusbphy_set_mode(struct phynode *phynode, int mode)
41284ebe16aSEmmanuel Vadot {
41384ebe16aSEmmanuel Vadot 	device_t dev;
41484ebe16aSEmmanuel Vadot 	intptr_t phy;
41584ebe16aSEmmanuel Vadot 	struct awusbphy_softc *sc;
41684ebe16aSEmmanuel Vadot 	uint32_t val;
41784ebe16aSEmmanuel Vadot 	int error, vbus_det;
41884ebe16aSEmmanuel Vadot 
41984ebe16aSEmmanuel Vadot 	dev = phynode_get_device(phynode);
42084ebe16aSEmmanuel Vadot 	phy = phynode_get_id(phynode);
42184ebe16aSEmmanuel Vadot 	sc = device_get_softc(dev);
42284ebe16aSEmmanuel Vadot 
423198e1debSEmmanuel Vadot 	if (phy != 0) {
424198e1debSEmmanuel Vadot 		if (mode != PHY_USB_MODE_HOST)
42584ebe16aSEmmanuel Vadot 			return (EINVAL);
426198e1debSEmmanuel Vadot 		return (0);
427198e1debSEmmanuel Vadot 	}
42884ebe16aSEmmanuel Vadot 
429aea49d9fSAndriy Gapon 	if (sc->mode == mode)
430aea49d9fSAndriy Gapon 		return (0);
431aea49d9fSAndriy Gapon 	if (mode == PHY_USB_MODE_OTG)	/* TODO */
432aea49d9fSAndriy Gapon 		return (EOPNOTSUPP);
433aea49d9fSAndriy Gapon 
434aea49d9fSAndriy Gapon 	error = awusbphy_vbus_detect(dev, &vbus_det);
435aea49d9fSAndriy Gapon 	if (error != 0)
436aea49d9fSAndriy Gapon 		return (error);
437aea49d9fSAndriy Gapon 
43884ebe16aSEmmanuel Vadot 	val = bus_read_4(sc->phy_ctrl, PHY_CSR);
43984ebe16aSEmmanuel Vadot 	val &= ~(VBUS_CHANGE_DET | ID_CHANGE_DET | DPDM_CHANGE_DET);
44084ebe16aSEmmanuel Vadot 	val |= (ID_PULLUP_EN | DPDM_PULLUP_EN);
44184ebe16aSEmmanuel Vadot 	val &= ~FORCE_VBUS_VALID;
442aea49d9fSAndriy Gapon 	val |= (vbus_det ? FORCE_VBUS_VALID_HIGH : FORCE_VBUS_VALID_LOW) <<
443aea49d9fSAndriy Gapon 	    FORCE_VBUS_VALID_SHIFT;
444aea49d9fSAndriy Gapon 	val &= ~FORCE_ID;
445aea49d9fSAndriy Gapon 
446aea49d9fSAndriy Gapon 	switch (mode) {
447aea49d9fSAndriy Gapon 	case PHY_USB_MODE_HOST:
448aea49d9fSAndriy Gapon 		val |= (FORCE_ID_LOW << FORCE_ID_SHIFT);
449aea49d9fSAndriy Gapon 		if (sc->phy_conf->phy0_route)
450aea49d9fSAndriy Gapon 			CLR4(sc->phy_ctrl, OTG_PHY_CFG, OTG_PHY_ROUTE_OTG);
451aea49d9fSAndriy Gapon 		break;
452aea49d9fSAndriy Gapon 	case PHY_USB_MODE_DEVICE:
453aea49d9fSAndriy Gapon 		val |= (FORCE_ID_HIGH << FORCE_ID_SHIFT);
454aea49d9fSAndriy Gapon 		if (sc->phy_conf->phy0_route)
455aea49d9fSAndriy Gapon 			SET4(sc->phy_ctrl, OTG_PHY_CFG, OTG_PHY_ROUTE_OTG);
456aea49d9fSAndriy Gapon 		break;
457aea49d9fSAndriy Gapon 	default:
458aea49d9fSAndriy Gapon 		return (EINVAL);
459aea49d9fSAndriy Gapon 	}
460aea49d9fSAndriy Gapon 
46184ebe16aSEmmanuel Vadot 	bus_write_4(sc->phy_ctrl, PHY_CSR, val);
46284ebe16aSEmmanuel Vadot 	sc->mode = mode;
46384ebe16aSEmmanuel Vadot 	return (0);
46484ebe16aSEmmanuel Vadot }
46584ebe16aSEmmanuel Vadot 
46684ebe16aSEmmanuel Vadot static int
awusbphy_probe(device_t dev)4671b4bd023SJared McNeill awusbphy_probe(device_t dev)
4681b4bd023SJared McNeill {
4691b4bd023SJared McNeill 	if (!ofw_bus_status_okay(dev))
4701b4bd023SJared McNeill 		return (ENXIO);
4711b4bd023SJared McNeill 
4721b4bd023SJared McNeill 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
4731b4bd023SJared McNeill 		return (ENXIO);
4741b4bd023SJared McNeill 
4751b4bd023SJared McNeill 	device_set_desc(dev, "Allwinner USB PHY");
4761b4bd023SJared McNeill 	return (BUS_PROBE_DEFAULT);
4771b4bd023SJared McNeill }
4781b4bd023SJared McNeill 
4791b4bd023SJared McNeill static int
awusbphy_attach(device_t dev)4801b4bd023SJared McNeill awusbphy_attach(device_t dev)
4811b4bd023SJared McNeill {
4821b4bd023SJared McNeill 	int error;
483f8759facSMichal Meloun 	struct phynode *phynode;
484f8759facSMichal Meloun 	struct phynode_init_def phy_init;
485f8759facSMichal Meloun 	struct awusbphy_softc *sc;
486f8759facSMichal Meloun 	int i;
4871b4bd023SJared McNeill 
488f8759facSMichal Meloun 	sc = device_get_softc(dev);
4891b4bd023SJared McNeill 	error = awusbphy_init(dev);
490627c360fSJared McNeill 	if (error) {
4911b4bd023SJared McNeill 		device_printf(dev, "failed to initialize USB PHY, error %d\n",
4921b4bd023SJared McNeill 		    error);
493627c360fSJared McNeill 		return (error);
494627c360fSJared McNeill 	}
495627c360fSJared McNeill 
496f8759facSMichal Meloun 	/* Create and register phys. */
497f8759facSMichal Meloun 	for (i = 0; i < sc->phy_conf->num_phys; i++) {
498f8759facSMichal Meloun 		bzero(&phy_init, sizeof(phy_init));
499f8759facSMichal Meloun 		phy_init.id = i;
500f8759facSMichal Meloun 		phy_init.ofw_node = ofw_bus_get_node(dev);
501f8759facSMichal Meloun 		phynode = phynode_create(dev, &awusbphy_phynode_class,
502f8759facSMichal Meloun 		    &phy_init);
503f8759facSMichal Meloun 		if (phynode == NULL) {
504f8759facSMichal Meloun 			device_printf(dev, "failed to create USB PHY\n");
505f8759facSMichal Meloun 			return (ENXIO);
506f8759facSMichal Meloun 		}
507f8759facSMichal Meloun 		if (phynode_register(phynode) == NULL) {
508f8759facSMichal Meloun 			device_printf(dev, "failed to create USB PHY\n");
509f8759facSMichal Meloun 			return (ENXIO);
510f8759facSMichal Meloun 		}
511f8759facSMichal Meloun 	}
5121b4bd023SJared McNeill 
5131b4bd023SJared McNeill 	return (error);
5141b4bd023SJared McNeill }
5151b4bd023SJared McNeill 
5161b4bd023SJared McNeill static device_method_t awusbphy_methods[] = {
5171b4bd023SJared McNeill 	/* Device interface */
5181b4bd023SJared McNeill 	DEVMETHOD(device_probe,		awusbphy_probe),
5191b4bd023SJared McNeill 	DEVMETHOD(device_attach,	awusbphy_attach),
5201b4bd023SJared McNeill 
5211b4bd023SJared McNeill 	DEVMETHOD_END
5221b4bd023SJared McNeill };
5231b4bd023SJared McNeill 
5241b4bd023SJared McNeill static driver_t awusbphy_driver = {
5251b4bd023SJared McNeill 	"awusbphy",
5261b4bd023SJared McNeill 	awusbphy_methods,
527627c360fSJared McNeill 	sizeof(struct awusbphy_softc)
5281b4bd023SJared McNeill };
5291b4bd023SJared McNeill 
5309f3b3133SKyle Evans /* aw_usbphy needs to come up after regulators/gpio/etc, but before ehci/ohci */
5317e1e2ba1SJohn Baldwin EARLY_DRIVER_MODULE(awusbphy, simplebus, awusbphy_driver, 0, 0,
5327e1e2ba1SJohn Baldwin     BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
5331b4bd023SJared McNeill MODULE_VERSION(awusbphy, 1);
534