xref: /freebsd/sys/dev/usb/controller/usb_nop_xceiv.c (revision bc9372d784c2bd4874f221bf7cd8072cb157dc71)
1aa7c3aa6SEmmanuel Vadot /*-
2aa7c3aa6SEmmanuel Vadot  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3aa7c3aa6SEmmanuel Vadot  *
4aa7c3aa6SEmmanuel Vadot  * Copyright (c) 2019 Rubicon Communications, LLC (Netgate)
5aa7c3aa6SEmmanuel Vadot  *
6aa7c3aa6SEmmanuel Vadot  * Redistribution and use in source and binary forms, with or without
7aa7c3aa6SEmmanuel Vadot  * modification, are permitted provided that the following conditions
8aa7c3aa6SEmmanuel Vadot  * are met:
9aa7c3aa6SEmmanuel Vadot  * 1. Redistributions of source code must retain the above copyright
10aa7c3aa6SEmmanuel Vadot  *    notice, this list of conditions and the following disclaimer.
11aa7c3aa6SEmmanuel Vadot  * 2. Redistributions in binary form must reproduce the above copyright
12aa7c3aa6SEmmanuel Vadot  *    notice, this list of conditions and the following disclaimer in the
13aa7c3aa6SEmmanuel Vadot  *    documentation and/or other materials provided with the distribution.
14aa7c3aa6SEmmanuel Vadot  *
15aa7c3aa6SEmmanuel Vadot  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16aa7c3aa6SEmmanuel Vadot  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17aa7c3aa6SEmmanuel Vadot  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18aa7c3aa6SEmmanuel Vadot  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19aa7c3aa6SEmmanuel Vadot  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20aa7c3aa6SEmmanuel Vadot  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21aa7c3aa6SEmmanuel Vadot  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22aa7c3aa6SEmmanuel Vadot  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23aa7c3aa6SEmmanuel Vadot  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24aa7c3aa6SEmmanuel Vadot  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25aa7c3aa6SEmmanuel Vadot  * SUCH DAMAGE.
26aa7c3aa6SEmmanuel Vadot  *
27aa7c3aa6SEmmanuel Vadot  */
28aa7c3aa6SEmmanuel Vadot 
29aa7c3aa6SEmmanuel Vadot #include <sys/cdefs.h>
30aa7c3aa6SEmmanuel Vadot __FBSDID("$FreeBSD$");
31aa7c3aa6SEmmanuel Vadot 
32aa7c3aa6SEmmanuel Vadot #include <sys/param.h>
33aa7c3aa6SEmmanuel Vadot #include <sys/systm.h>
34aa7c3aa6SEmmanuel Vadot #include <sys/bus.h>
35aa7c3aa6SEmmanuel Vadot 
36aa7c3aa6SEmmanuel Vadot #include <sys/kernel.h>
37aa7c3aa6SEmmanuel Vadot #include <sys/module.h>
38aa7c3aa6SEmmanuel Vadot #include <sys/rman.h>
39aa7c3aa6SEmmanuel Vadot #include <sys/lock.h>
40aa7c3aa6SEmmanuel Vadot #include <sys/mutex.h>
41aa7c3aa6SEmmanuel Vadot 
42aa7c3aa6SEmmanuel Vadot #include <machine/bus.h>
43aa7c3aa6SEmmanuel Vadot 
44aa7c3aa6SEmmanuel Vadot #include <dev/ofw/ofw_bus.h>
45aa7c3aa6SEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h>
46aa7c3aa6SEmmanuel Vadot 
47aa7c3aa6SEmmanuel Vadot #include <dev/extres/clk/clk.h>
48aa7c3aa6SEmmanuel Vadot #include <dev/extres/regulator/regulator.h>
49aa7c3aa6SEmmanuel Vadot #include <dev/extres/phy/phy_usb.h>
50aa7c3aa6SEmmanuel Vadot 
51aa7c3aa6SEmmanuel Vadot #include "phynode_if.h"
52aa7c3aa6SEmmanuel Vadot 
53aa7c3aa6SEmmanuel Vadot struct usb_nop_xceiv_softc {
54aa7c3aa6SEmmanuel Vadot 	device_t		dev;
55aa7c3aa6SEmmanuel Vadot 	regulator_t		vcc_supply;
56aa7c3aa6SEmmanuel Vadot 	clk_t			clk;
57aa7c3aa6SEmmanuel Vadot 	uint32_t		clk_freq;
58aa7c3aa6SEmmanuel Vadot };
59aa7c3aa6SEmmanuel Vadot 
60aa7c3aa6SEmmanuel Vadot static struct ofw_compat_data compat_data[] = {
61aa7c3aa6SEmmanuel Vadot 	{"usb-nop-xceiv", 1},
62aa7c3aa6SEmmanuel Vadot 	{NULL,            0}
63aa7c3aa6SEmmanuel Vadot };
64aa7c3aa6SEmmanuel Vadot 
65aa7c3aa6SEmmanuel Vadot /* Phy class and methods. */
66aa7c3aa6SEmmanuel Vadot static int usb_nop_xceiv_phy_enable(struct phynode *phy, bool enable);
67aa7c3aa6SEmmanuel Vadot static phynode_usb_method_t usb_nop_xceiv_phynode_methods[] = {
68aa7c3aa6SEmmanuel Vadot 	PHYNODEMETHOD(phynode_enable, usb_nop_xceiv_phy_enable),
69aa7c3aa6SEmmanuel Vadot 
70aa7c3aa6SEmmanuel Vadot 	PHYNODEMETHOD_END
71aa7c3aa6SEmmanuel Vadot };
72aa7c3aa6SEmmanuel Vadot DEFINE_CLASS_1(usb_nop_xceiv_phynode, usb_nop_xceiv_phynode_class,
73aa7c3aa6SEmmanuel Vadot     usb_nop_xceiv_phynode_methods,
74aa7c3aa6SEmmanuel Vadot     sizeof(struct phynode_usb_sc), phynode_usb_class);
75aa7c3aa6SEmmanuel Vadot 
76aa7c3aa6SEmmanuel Vadot static int
77aa7c3aa6SEmmanuel Vadot usb_nop_xceiv_phy_enable(struct phynode *phynode, bool enable)
78aa7c3aa6SEmmanuel Vadot {
79aa7c3aa6SEmmanuel Vadot 	struct usb_nop_xceiv_softc *sc;
80aa7c3aa6SEmmanuel Vadot 	device_t dev;
81aa7c3aa6SEmmanuel Vadot 	intptr_t phy;
82aa7c3aa6SEmmanuel Vadot 	int error;
83aa7c3aa6SEmmanuel Vadot 
84aa7c3aa6SEmmanuel Vadot 	dev = phynode_get_device(phynode);
85aa7c3aa6SEmmanuel Vadot 	phy = phynode_get_id(phynode);
86aa7c3aa6SEmmanuel Vadot 	sc = device_get_softc(dev);
87aa7c3aa6SEmmanuel Vadot 
88aa7c3aa6SEmmanuel Vadot 	if (phy != 0)
89aa7c3aa6SEmmanuel Vadot 		return (ERANGE);
90aa7c3aa6SEmmanuel Vadot 
91aa7c3aa6SEmmanuel Vadot 	/* Enable the phy clock */
92aa7c3aa6SEmmanuel Vadot 	if (sc->clk_freq != 0) {
93aa7c3aa6SEmmanuel Vadot 		if (enable) {
94aa7c3aa6SEmmanuel Vadot 			error = clk_set_freq(sc->clk, sc->clk_freq,
95aa7c3aa6SEmmanuel Vadot 			  CLK_SET_ROUND_ANY);
96aa7c3aa6SEmmanuel Vadot 			if (error != 0) {
97aa7c3aa6SEmmanuel Vadot 				device_printf(dev, "Cannot set clock to %dMhz\n",
98aa7c3aa6SEmmanuel Vadot 				  sc->clk_freq);
99aa7c3aa6SEmmanuel Vadot 				goto fail;
100aa7c3aa6SEmmanuel Vadot 			}
101aa7c3aa6SEmmanuel Vadot 
102aa7c3aa6SEmmanuel Vadot 			error = clk_enable(sc->clk);
103aa7c3aa6SEmmanuel Vadot 		} else
104aa7c3aa6SEmmanuel Vadot 			error = clk_disable(sc->clk);
105aa7c3aa6SEmmanuel Vadot 
106aa7c3aa6SEmmanuel Vadot 		if (error != 0) {
107aa7c3aa6SEmmanuel Vadot 			device_printf(dev, "Cannot %sable the clock\n",
108aa7c3aa6SEmmanuel Vadot 			    enable ? "En" : "Dis");
109aa7c3aa6SEmmanuel Vadot 			goto fail;
110aa7c3aa6SEmmanuel Vadot 		}
111aa7c3aa6SEmmanuel Vadot 	}
112aa7c3aa6SEmmanuel Vadot 	if (sc->vcc_supply) {
113aa7c3aa6SEmmanuel Vadot 		if (enable)
114aa7c3aa6SEmmanuel Vadot 			error = regulator_enable(sc->vcc_supply);
115aa7c3aa6SEmmanuel Vadot 		else
116aa7c3aa6SEmmanuel Vadot 			error = regulator_disable(sc->vcc_supply);
117aa7c3aa6SEmmanuel Vadot 		if (error != 0) {
118aa7c3aa6SEmmanuel Vadot 			device_printf(dev, "Cannot %sable the regulator\n",
119aa7c3aa6SEmmanuel Vadot 			    enable ? "En" : "Dis");
120aa7c3aa6SEmmanuel Vadot 			goto fail;
121aa7c3aa6SEmmanuel Vadot 		}
122aa7c3aa6SEmmanuel Vadot 	}
123aa7c3aa6SEmmanuel Vadot 
124aa7c3aa6SEmmanuel Vadot 	return (0);
125aa7c3aa6SEmmanuel Vadot 
126aa7c3aa6SEmmanuel Vadot fail:
127aa7c3aa6SEmmanuel Vadot 	return (ENXIO);
128aa7c3aa6SEmmanuel Vadot }
129aa7c3aa6SEmmanuel Vadot 
130aa7c3aa6SEmmanuel Vadot static int
131aa7c3aa6SEmmanuel Vadot usb_nop_xceiv_probe(device_t dev)
132aa7c3aa6SEmmanuel Vadot {
133aa7c3aa6SEmmanuel Vadot 
134aa7c3aa6SEmmanuel Vadot 	if (!ofw_bus_status_okay(dev))
135aa7c3aa6SEmmanuel Vadot 		return (ENXIO);
136aa7c3aa6SEmmanuel Vadot 
137aa7c3aa6SEmmanuel Vadot 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
138aa7c3aa6SEmmanuel Vadot 		return (ENXIO);
139aa7c3aa6SEmmanuel Vadot 
140aa7c3aa6SEmmanuel Vadot 	device_set_desc(dev, "USB NOP PHY");
141aa7c3aa6SEmmanuel Vadot 	return (BUS_PROBE_DEFAULT);
142aa7c3aa6SEmmanuel Vadot }
143aa7c3aa6SEmmanuel Vadot 
144aa7c3aa6SEmmanuel Vadot static int
145aa7c3aa6SEmmanuel Vadot usb_nop_xceiv_attach(device_t dev)
146aa7c3aa6SEmmanuel Vadot {
147aa7c3aa6SEmmanuel Vadot 	struct usb_nop_xceiv_softc *sc;
148aa7c3aa6SEmmanuel Vadot 	struct phynode *phynode;
149aa7c3aa6SEmmanuel Vadot 	struct phynode_init_def phy_init;
150aa7c3aa6SEmmanuel Vadot 	phandle_t node;
151aa7c3aa6SEmmanuel Vadot 	int error;
152aa7c3aa6SEmmanuel Vadot 
153aa7c3aa6SEmmanuel Vadot 	sc = device_get_softc(dev);
154aa7c3aa6SEmmanuel Vadot 	sc->dev = dev;
155aa7c3aa6SEmmanuel Vadot 	node = ofw_bus_get_node(dev);
156aa7c3aa6SEmmanuel Vadot 
157aa7c3aa6SEmmanuel Vadot 	/* Parse the optional properties */
158aa7c3aa6SEmmanuel Vadot 	OF_getencprop(node, "clock-frequency", &sc->clk_freq, sizeof(uint32_t));
159aa7c3aa6SEmmanuel Vadot 
160aa7c3aa6SEmmanuel Vadot 	error = clk_get_by_ofw_name(dev, node, "main_clk", &sc->clk);
161aa7c3aa6SEmmanuel Vadot 	if (error != 0 && sc->clk_freq != 0) {
162aa7c3aa6SEmmanuel Vadot 		device_printf(dev, "clock property is mandatory if clock-frequency is present\n");
163aa7c3aa6SEmmanuel Vadot 		return (ENXIO);
164aa7c3aa6SEmmanuel Vadot 	}
165aa7c3aa6SEmmanuel Vadot 
166aa7c3aa6SEmmanuel Vadot 	regulator_get_by_ofw_property(dev, node, "vcc-supply", &sc->vcc_supply);
167aa7c3aa6SEmmanuel Vadot 
168aa7c3aa6SEmmanuel Vadot 	phy_init.id = 0;
169aa7c3aa6SEmmanuel Vadot 	phy_init.ofw_node = node;
170aa7c3aa6SEmmanuel Vadot 	phynode = phynode_create(dev, &usb_nop_xceiv_phynode_class,
171aa7c3aa6SEmmanuel Vadot 	    &phy_init);
172aa7c3aa6SEmmanuel Vadot 	if (phynode == NULL) {
173aa7c3aa6SEmmanuel Vadot 		device_printf(dev, "failed to create USB NOP PHY\n");
174aa7c3aa6SEmmanuel Vadot 		return (ENXIO);
175aa7c3aa6SEmmanuel Vadot 	}
176aa7c3aa6SEmmanuel Vadot 	if (phynode_register(phynode) == NULL) {
177aa7c3aa6SEmmanuel Vadot 		device_printf(dev, "failed to create USB NOP PHY\n");
178aa7c3aa6SEmmanuel Vadot 		return (ENXIO);
179aa7c3aa6SEmmanuel Vadot 	}
180aa7c3aa6SEmmanuel Vadot 
181aa7c3aa6SEmmanuel Vadot 	OF_device_register_xref(OF_xref_from_node(node), dev);
182aa7c3aa6SEmmanuel Vadot 
183aa7c3aa6SEmmanuel Vadot 	return (0);
184aa7c3aa6SEmmanuel Vadot }
185aa7c3aa6SEmmanuel Vadot 
186aa7c3aa6SEmmanuel Vadot static int
187aa7c3aa6SEmmanuel Vadot usb_nop_xceiv_detach(device_t dev)
188aa7c3aa6SEmmanuel Vadot {
189aa7c3aa6SEmmanuel Vadot 
190aa7c3aa6SEmmanuel Vadot 	return (EBUSY);
191aa7c3aa6SEmmanuel Vadot }
192aa7c3aa6SEmmanuel Vadot 
193aa7c3aa6SEmmanuel Vadot static device_method_t usb_nop_xceiv_methods[] = {
194aa7c3aa6SEmmanuel Vadot 	/* Device interface */
195aa7c3aa6SEmmanuel Vadot 	DEVMETHOD(device_probe, usb_nop_xceiv_probe),
196aa7c3aa6SEmmanuel Vadot 	DEVMETHOD(device_attach, usb_nop_xceiv_attach),
197aa7c3aa6SEmmanuel Vadot 	DEVMETHOD(device_detach, usb_nop_xceiv_detach),
198aa7c3aa6SEmmanuel Vadot 
199aa7c3aa6SEmmanuel Vadot 	DEVMETHOD_END
200aa7c3aa6SEmmanuel Vadot };
201aa7c3aa6SEmmanuel Vadot 
202aa7c3aa6SEmmanuel Vadot static driver_t usb_nop_xceiv_driver = {
203aa7c3aa6SEmmanuel Vadot 	"usb_nop_xceiv",
204aa7c3aa6SEmmanuel Vadot 	usb_nop_xceiv_methods,
205aa7c3aa6SEmmanuel Vadot 	sizeof(struct usb_nop_xceiv_softc),
206aa7c3aa6SEmmanuel Vadot };
207aa7c3aa6SEmmanuel Vadot 
208aa7c3aa6SEmmanuel Vadot EARLY_DRIVER_MODULE(usb_nop_xceiv, simplebus, usb_nop_xceiv_driver,
209*bc9372d7SJohn Baldwin     0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
210