xref: /freebsd/sys/arm/qualcomm/ipq4018_usb_hs_phy.c (revision 86f0c3ec13aa6d583a586f343329d15faf22bd75)
1*86f0c3ecSAdrian Chadd /*-
2*86f0c3ecSAdrian Chadd  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*86f0c3ecSAdrian Chadd  *
4*86f0c3ecSAdrian Chadd  * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org>
5*86f0c3ecSAdrian Chadd  *
6*86f0c3ecSAdrian Chadd  * Redistribution and use in source and binary forms, with or without
7*86f0c3ecSAdrian Chadd  * modification, are permitted provided that the following conditions
8*86f0c3ecSAdrian Chadd  * are met:
9*86f0c3ecSAdrian Chadd  * 1. Redistributions of source code must retain the above copyright
10*86f0c3ecSAdrian Chadd  *    notice, this list of conditions and the following disclaimer.
11*86f0c3ecSAdrian Chadd  * 2. Redistributions in binary form must reproduce the above copyright
12*86f0c3ecSAdrian Chadd  *    notice, this list of conditions and the following disclaimer in the
13*86f0c3ecSAdrian Chadd  *    documentation and/or other materials provided with the distribution.
14*86f0c3ecSAdrian Chadd  *
15*86f0c3ecSAdrian Chadd  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16*86f0c3ecSAdrian Chadd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17*86f0c3ecSAdrian Chadd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18*86f0c3ecSAdrian Chadd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19*86f0c3ecSAdrian Chadd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20*86f0c3ecSAdrian Chadd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21*86f0c3ecSAdrian Chadd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22*86f0c3ecSAdrian Chadd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23*86f0c3ecSAdrian Chadd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24*86f0c3ecSAdrian Chadd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25*86f0c3ecSAdrian Chadd  * SUCH DAMAGE.
26*86f0c3ecSAdrian Chadd  */
27*86f0c3ecSAdrian Chadd 
28*86f0c3ecSAdrian Chadd #include <sys/cdefs.h>
29*86f0c3ecSAdrian Chadd __FBSDID("$FreeBSD$");
30*86f0c3ecSAdrian Chadd 
31*86f0c3ecSAdrian Chadd #include <sys/param.h>
32*86f0c3ecSAdrian Chadd #include <sys/systm.h>
33*86f0c3ecSAdrian Chadd #include <sys/bus.h>
34*86f0c3ecSAdrian Chadd #include <sys/gpio.h>
35*86f0c3ecSAdrian Chadd #include <sys/kernel.h>
36*86f0c3ecSAdrian Chadd #include <sys/module.h>
37*86f0c3ecSAdrian Chadd #include <sys/malloc.h>
38*86f0c3ecSAdrian Chadd #include <sys/rman.h>
39*86f0c3ecSAdrian Chadd 
40*86f0c3ecSAdrian Chadd #include <machine/bus.h>
41*86f0c3ecSAdrian Chadd 
42*86f0c3ecSAdrian Chadd #include <dev/extres/hwreset/hwreset.h>
43*86f0c3ecSAdrian Chadd #include <dev/extres/phy/phy_usb.h>
44*86f0c3ecSAdrian Chadd #include <dev/extres/regulator/regulator.h>
45*86f0c3ecSAdrian Chadd #include <dev/ofw/ofw_bus.h>
46*86f0c3ecSAdrian Chadd #include <dev/ofw/ofw_bus_subr.h>
47*86f0c3ecSAdrian Chadd 
48*86f0c3ecSAdrian Chadd #include <dev/fdt/simple_mfd.h>
49*86f0c3ecSAdrian Chadd #include "phynode_if.h"
50*86f0c3ecSAdrian Chadd #include "phynode_usb_if.h"
51*86f0c3ecSAdrian Chadd 
52*86f0c3ecSAdrian Chadd static struct ofw_compat_data compat_data[] = {
53*86f0c3ecSAdrian Chadd 	{"qcom,usb-hs-ipq4019-phy",	1},
54*86f0c3ecSAdrian Chadd 	{NULL,				0},
55*86f0c3ecSAdrian Chadd };
56*86f0c3ecSAdrian Chadd 
57*86f0c3ecSAdrian Chadd struct ipq4018_usb_hs_phy_softc {
58*86f0c3ecSAdrian Chadd 	device_t		dev;
59*86f0c3ecSAdrian Chadd };
60*86f0c3ecSAdrian Chadd 
61*86f0c3ecSAdrian Chadd struct ipq4018_usb_hs_phynode_sc {
62*86f0c3ecSAdrian Chadd 	struct phynode_usb_sc	usb_sc;
63*86f0c3ecSAdrian Chadd 	int			mode;
64*86f0c3ecSAdrian Chadd 	hwreset_t		por_rst;
65*86f0c3ecSAdrian Chadd 	hwreset_t		srif_rst;
66*86f0c3ecSAdrian Chadd };
67*86f0c3ecSAdrian Chadd 
68*86f0c3ecSAdrian Chadd static int
69*86f0c3ecSAdrian Chadd ipq4018_usb_hs_phynode_phy_enable(struct phynode *phynode, bool enable)
70*86f0c3ecSAdrian Chadd {
71*86f0c3ecSAdrian Chadd 	struct ipq4018_usb_hs_phynode_sc *sc;
72*86f0c3ecSAdrian Chadd 	device_t dev;
73*86f0c3ecSAdrian Chadd 	int rv;
74*86f0c3ecSAdrian Chadd 
75*86f0c3ecSAdrian Chadd 	dev = phynode_get_device(phynode);
76*86f0c3ecSAdrian Chadd 	sc = phynode_get_softc(phynode);
77*86f0c3ecSAdrian Chadd 
78*86f0c3ecSAdrian Chadd 	/*
79*86f0c3ecSAdrian Chadd 	 *
80*86f0c3ecSAdrian Chadd 	 * For power-off - assert por, sleep for 10ms, assert srif,
81*86f0c3ecSAdrian Chadd 	 * sleep for 10ms
82*86f0c3ecSAdrian Chadd 	 */
83*86f0c3ecSAdrian Chadd 	rv = hwreset_assert(sc->por_rst);
84*86f0c3ecSAdrian Chadd 	if (rv != 0)
85*86f0c3ecSAdrian Chadd 		goto done;
86*86f0c3ecSAdrian Chadd 	DELAY(10*1000);
87*86f0c3ecSAdrian Chadd 	rv = hwreset_assert(sc->srif_rst);
88*86f0c3ecSAdrian Chadd 	if (rv != 0)
89*86f0c3ecSAdrian Chadd 		goto done;
90*86f0c3ecSAdrian Chadd 	DELAY(10*1000);
91*86f0c3ecSAdrian Chadd 
92*86f0c3ecSAdrian Chadd 	/*
93*86f0c3ecSAdrian Chadd 	 * For power-on - power off first, then deassert srif, then
94*86f0c3ecSAdrian Chadd 	 * sleep for 10ms, then deassert por.
95*86f0c3ecSAdrian Chadd 	 */
96*86f0c3ecSAdrian Chadd 	if (enable) {
97*86f0c3ecSAdrian Chadd 		rv = hwreset_deassert(sc->srif_rst);
98*86f0c3ecSAdrian Chadd 		if (rv != 0)
99*86f0c3ecSAdrian Chadd 			goto done;
100*86f0c3ecSAdrian Chadd 		DELAY(10*1000);
101*86f0c3ecSAdrian Chadd 		rv = hwreset_deassert(sc->por_rst);
102*86f0c3ecSAdrian Chadd 		if (rv != 0)
103*86f0c3ecSAdrian Chadd 			goto done;
104*86f0c3ecSAdrian Chadd 		DELAY(10*1000);
105*86f0c3ecSAdrian Chadd 	}
106*86f0c3ecSAdrian Chadd 
107*86f0c3ecSAdrian Chadd done:
108*86f0c3ecSAdrian Chadd 	if (rv != 0) {
109*86f0c3ecSAdrian Chadd 		device_printf(dev, "%s: failed, rv=%d\n", __func__, rv);
110*86f0c3ecSAdrian Chadd 	}
111*86f0c3ecSAdrian Chadd 	return (rv);
112*86f0c3ecSAdrian Chadd }
113*86f0c3ecSAdrian Chadd 
114*86f0c3ecSAdrian Chadd  /* Phy controller class and methods. */
115*86f0c3ecSAdrian Chadd static phynode_method_t ipq4018_usb_hs_phynode_methods[] = {
116*86f0c3ecSAdrian Chadd 	PHYNODEUSBMETHOD(phynode_enable,	ipq4018_usb_hs_phynode_phy_enable),
117*86f0c3ecSAdrian Chadd 	PHYNODEUSBMETHOD_END
118*86f0c3ecSAdrian Chadd };
119*86f0c3ecSAdrian Chadd DEFINE_CLASS_1(ipq4018_usb_hs_phynode, ipq4018_usb_hs_phynode_class,
120*86f0c3ecSAdrian Chadd     ipq4018_usb_hs_phynode_methods,
121*86f0c3ecSAdrian Chadd     sizeof(struct ipq4018_usb_hs_phynode_sc), phynode_usb_class);
122*86f0c3ecSAdrian Chadd 
123*86f0c3ecSAdrian Chadd static int
124*86f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_init_phy(struct ipq4018_usb_hs_phy_softc *sc,
125*86f0c3ecSAdrian Chadd     phandle_t node)
126*86f0c3ecSAdrian Chadd {
127*86f0c3ecSAdrian Chadd 	struct phynode *phynode;
128*86f0c3ecSAdrian Chadd 	struct phynode_init_def phy_init;
129*86f0c3ecSAdrian Chadd 	struct ipq4018_usb_hs_phynode_sc *phy_sc;
130*86f0c3ecSAdrian Chadd 	int rv;
131*86f0c3ecSAdrian Chadd 	hwreset_t por_rst = NULL, srif_rst = NULL;
132*86f0c3ecSAdrian Chadd 
133*86f0c3ecSAdrian Chadd 	/* FDT resources */
134*86f0c3ecSAdrian Chadd 	rv = hwreset_get_by_ofw_name(sc->dev, node, "por_rst", &por_rst);
135*86f0c3ecSAdrian Chadd 	if (rv != 0 && rv != ENOENT) {
136*86f0c3ecSAdrian Chadd 		device_printf(sc->dev, "Cannot get 'por_rst' reset\n");
137*86f0c3ecSAdrian Chadd 		goto fail;
138*86f0c3ecSAdrian Chadd 	}
139*86f0c3ecSAdrian Chadd 	rv = hwreset_get_by_ofw_name(sc->dev, node, "srif_rst", &srif_rst);
140*86f0c3ecSAdrian Chadd 	if (rv != 0 && rv != ENOENT) {
141*86f0c3ecSAdrian Chadd 		device_printf(sc->dev, "Cannot get 'srif_rst' reset\n");
142*86f0c3ecSAdrian Chadd 		goto fail;
143*86f0c3ecSAdrian Chadd 	}
144*86f0c3ecSAdrian Chadd 
145*86f0c3ecSAdrian Chadd 	/* Create and register phy. */
146*86f0c3ecSAdrian Chadd 	bzero(&phy_init, sizeof(phy_init));
147*86f0c3ecSAdrian Chadd 	phy_init.id = 1;
148*86f0c3ecSAdrian Chadd 	phy_init.ofw_node = node;
149*86f0c3ecSAdrian Chadd 	phynode = phynode_create(sc->dev, &ipq4018_usb_hs_phynode_class,
150*86f0c3ecSAdrian Chadd 	    &phy_init);
151*86f0c3ecSAdrian Chadd 	if (phynode == NULL) {
152*86f0c3ecSAdrian Chadd 		device_printf(sc->dev, "Cannot create phy.\n");
153*86f0c3ecSAdrian Chadd 		return (ENXIO);
154*86f0c3ecSAdrian Chadd 	}
155*86f0c3ecSAdrian Chadd 
156*86f0c3ecSAdrian Chadd 	phy_sc = phynode_get_softc(phynode);
157*86f0c3ecSAdrian Chadd 	phy_sc->por_rst = por_rst;
158*86f0c3ecSAdrian Chadd 	phy_sc->srif_rst = srif_rst;
159*86f0c3ecSAdrian Chadd 
160*86f0c3ecSAdrian Chadd 	if (phynode_register(phynode) == NULL) {
161*86f0c3ecSAdrian Chadd 		device_printf(sc->dev, "Cannot register phy.\n");
162*86f0c3ecSAdrian Chadd 		return (ENXIO);
163*86f0c3ecSAdrian Chadd 	}
164*86f0c3ecSAdrian Chadd 
165*86f0c3ecSAdrian Chadd 	(void) ipq4018_usb_hs_phynode_phy_enable(phynode, true);
166*86f0c3ecSAdrian Chadd 
167*86f0c3ecSAdrian Chadd 	return (0);
168*86f0c3ecSAdrian Chadd 
169*86f0c3ecSAdrian Chadd fail:
170*86f0c3ecSAdrian Chadd 	if (por_rst != NULL)
171*86f0c3ecSAdrian Chadd 		 hwreset_release(por_rst);
172*86f0c3ecSAdrian Chadd 	if (srif_rst != NULL)
173*86f0c3ecSAdrian Chadd 		 hwreset_release(srif_rst);
174*86f0c3ecSAdrian Chadd 
175*86f0c3ecSAdrian Chadd 	return (ENXIO);
176*86f0c3ecSAdrian Chadd }
177*86f0c3ecSAdrian Chadd 
178*86f0c3ecSAdrian Chadd static int
179*86f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_probe(device_t dev)
180*86f0c3ecSAdrian Chadd {
181*86f0c3ecSAdrian Chadd 
182*86f0c3ecSAdrian Chadd 	if (!ofw_bus_status_okay(dev))
183*86f0c3ecSAdrian Chadd 		return (ENXIO);
184*86f0c3ecSAdrian Chadd 
185*86f0c3ecSAdrian Chadd 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
186*86f0c3ecSAdrian Chadd 		return (ENXIO);
187*86f0c3ecSAdrian Chadd 
188*86f0c3ecSAdrian Chadd 	device_set_desc(dev, "IPQ4018/IPQ4019 USB HS PHY");
189*86f0c3ecSAdrian Chadd 	return (BUS_PROBE_DEFAULT);
190*86f0c3ecSAdrian Chadd }
191*86f0c3ecSAdrian Chadd 
192*86f0c3ecSAdrian Chadd static int
193*86f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_attach(device_t dev)
194*86f0c3ecSAdrian Chadd {
195*86f0c3ecSAdrian Chadd 	struct ipq4018_usb_hs_phy_softc *sc;
196*86f0c3ecSAdrian Chadd 	phandle_t node;
197*86f0c3ecSAdrian Chadd 	int rv;
198*86f0c3ecSAdrian Chadd 
199*86f0c3ecSAdrian Chadd 	sc = device_get_softc(dev);
200*86f0c3ecSAdrian Chadd 	sc->dev = dev;
201*86f0c3ecSAdrian Chadd 	node = ofw_bus_get_node(sc->dev);
202*86f0c3ecSAdrian Chadd 
203*86f0c3ecSAdrian Chadd 	rv = ipq4018_usb_hs_usbphy_init_phy(sc, node);
204*86f0c3ecSAdrian Chadd 	if (rv != 0)
205*86f0c3ecSAdrian Chadd 		goto fail;
206*86f0c3ecSAdrian Chadd 	return (bus_generic_attach(dev));
207*86f0c3ecSAdrian Chadd 
208*86f0c3ecSAdrian Chadd fail:
209*86f0c3ecSAdrian Chadd 	return (ENXIO);
210*86f0c3ecSAdrian Chadd }
211*86f0c3ecSAdrian Chadd 
212*86f0c3ecSAdrian Chadd static int
213*86f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_detach(device_t dev)
214*86f0c3ecSAdrian Chadd {
215*86f0c3ecSAdrian Chadd 	struct ipq4018_usb_hs_phy_softc *sc;
216*86f0c3ecSAdrian Chadd 	sc = device_get_softc(dev);
217*86f0c3ecSAdrian Chadd 
218*86f0c3ecSAdrian Chadd 	return (0);
219*86f0c3ecSAdrian Chadd }
220*86f0c3ecSAdrian Chadd 
221*86f0c3ecSAdrian Chadd static device_method_t ipq4018_usb_hs_usbphy_methods[] = {
222*86f0c3ecSAdrian Chadd 	/* Device interface */
223*86f0c3ecSAdrian Chadd 	DEVMETHOD(device_probe,			ipq4018_usb_hs_usbphy_probe),
224*86f0c3ecSAdrian Chadd 	DEVMETHOD(device_attach,		ipq4018_usb_hs_usbphy_attach),
225*86f0c3ecSAdrian Chadd 	DEVMETHOD(device_detach,		ipq4018_usb_hs_usbphy_detach),
226*86f0c3ecSAdrian Chadd 	DEVMETHOD_END
227*86f0c3ecSAdrian Chadd };
228*86f0c3ecSAdrian Chadd 
229*86f0c3ecSAdrian Chadd static devclass_t ipq4018_usb_hs_usbphy_devclass;
230*86f0c3ecSAdrian Chadd static DEFINE_CLASS_0(ipq4018_usb_hs_usbphy, ipq4018_usb_hs_usbphy_driver,
231*86f0c3ecSAdrian Chadd     ipq4018_usb_hs_usbphy_methods,
232*86f0c3ecSAdrian Chadd     sizeof(struct ipq4018_usb_hs_phy_softc));
233*86f0c3ecSAdrian Chadd EARLY_DRIVER_MODULE(ipq4018_usb_hs_usbphy, simplebus,
234*86f0c3ecSAdrian Chadd     ipq4018_usb_hs_usbphy_driver,
235*86f0c3ecSAdrian Chadd     ipq4018_usb_hs_usbphy_devclass, NULL, NULL,
236*86f0c3ecSAdrian Chadd     BUS_PASS_TIMER + BUS_PASS_ORDER_LAST);
237