1aa7c3aa6SEmmanuel Vadot /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
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/param.h>
30aa7c3aa6SEmmanuel Vadot #include <sys/systm.h>
31aa7c3aa6SEmmanuel Vadot #include <sys/bus.h>
32aa7c3aa6SEmmanuel Vadot
33aa7c3aa6SEmmanuel Vadot #include <sys/kernel.h>
34aa7c3aa6SEmmanuel Vadot #include <sys/module.h>
35aa7c3aa6SEmmanuel Vadot #include <sys/rman.h>
36aa7c3aa6SEmmanuel Vadot #include <sys/lock.h>
37aa7c3aa6SEmmanuel Vadot #include <sys/mutex.h>
38aa7c3aa6SEmmanuel Vadot
39aa7c3aa6SEmmanuel Vadot #include <machine/bus.h>
40aa7c3aa6SEmmanuel Vadot
41aa7c3aa6SEmmanuel Vadot #include <dev/ofw/ofw_bus.h>
42aa7c3aa6SEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h>
43aa7c3aa6SEmmanuel Vadot
44be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
45b2f0caf1SEmmanuel Vadot #include <dev/regulator/regulator.h>
46*950a6087SEmmanuel Vadot #include <dev/phy/phy_usb.h>
47aa7c3aa6SEmmanuel Vadot
48aa7c3aa6SEmmanuel Vadot #include "phynode_if.h"
49aa7c3aa6SEmmanuel Vadot
50aa7c3aa6SEmmanuel Vadot struct usb_nop_xceiv_softc {
51aa7c3aa6SEmmanuel Vadot device_t dev;
52aa7c3aa6SEmmanuel Vadot regulator_t vcc_supply;
53aa7c3aa6SEmmanuel Vadot clk_t clk;
54aa7c3aa6SEmmanuel Vadot uint32_t clk_freq;
55aa7c3aa6SEmmanuel Vadot };
56aa7c3aa6SEmmanuel Vadot
57aa7c3aa6SEmmanuel Vadot static struct ofw_compat_data compat_data[] = {
58aa7c3aa6SEmmanuel Vadot {"usb-nop-xceiv", 1},
59aa7c3aa6SEmmanuel Vadot {NULL, 0}
60aa7c3aa6SEmmanuel Vadot };
61aa7c3aa6SEmmanuel Vadot
62aa7c3aa6SEmmanuel Vadot /* Phy class and methods. */
63aa7c3aa6SEmmanuel Vadot static int usb_nop_xceiv_phy_enable(struct phynode *phy, bool enable);
64aa7c3aa6SEmmanuel Vadot static phynode_usb_method_t usb_nop_xceiv_phynode_methods[] = {
65aa7c3aa6SEmmanuel Vadot PHYNODEMETHOD(phynode_enable, usb_nop_xceiv_phy_enable),
66aa7c3aa6SEmmanuel Vadot
67aa7c3aa6SEmmanuel Vadot PHYNODEMETHOD_END
68aa7c3aa6SEmmanuel Vadot };
69aa7c3aa6SEmmanuel Vadot DEFINE_CLASS_1(usb_nop_xceiv_phynode, usb_nop_xceiv_phynode_class,
70aa7c3aa6SEmmanuel Vadot usb_nop_xceiv_phynode_methods,
71aa7c3aa6SEmmanuel Vadot sizeof(struct phynode_usb_sc), phynode_usb_class);
72aa7c3aa6SEmmanuel Vadot
73aa7c3aa6SEmmanuel Vadot static int
usb_nop_xceiv_phy_enable(struct phynode * phynode,bool enable)74aa7c3aa6SEmmanuel Vadot usb_nop_xceiv_phy_enable(struct phynode *phynode, bool enable)
75aa7c3aa6SEmmanuel Vadot {
76aa7c3aa6SEmmanuel Vadot struct usb_nop_xceiv_softc *sc;
77aa7c3aa6SEmmanuel Vadot device_t dev;
78aa7c3aa6SEmmanuel Vadot intptr_t phy;
79aa7c3aa6SEmmanuel Vadot int error;
80aa7c3aa6SEmmanuel Vadot
81aa7c3aa6SEmmanuel Vadot dev = phynode_get_device(phynode);
82aa7c3aa6SEmmanuel Vadot phy = phynode_get_id(phynode);
83aa7c3aa6SEmmanuel Vadot sc = device_get_softc(dev);
84aa7c3aa6SEmmanuel Vadot
85aa7c3aa6SEmmanuel Vadot if (phy != 0)
86aa7c3aa6SEmmanuel Vadot return (ERANGE);
87aa7c3aa6SEmmanuel Vadot
88aa7c3aa6SEmmanuel Vadot /* Enable the phy clock */
89aa7c3aa6SEmmanuel Vadot if (sc->clk_freq != 0) {
90aa7c3aa6SEmmanuel Vadot if (enable) {
91aa7c3aa6SEmmanuel Vadot error = clk_set_freq(sc->clk, sc->clk_freq,
92aa7c3aa6SEmmanuel Vadot CLK_SET_ROUND_ANY);
93aa7c3aa6SEmmanuel Vadot if (error != 0) {
94aa7c3aa6SEmmanuel Vadot device_printf(dev, "Cannot set clock to %dMhz\n",
95aa7c3aa6SEmmanuel Vadot sc->clk_freq);
96aa7c3aa6SEmmanuel Vadot goto fail;
97aa7c3aa6SEmmanuel Vadot }
98aa7c3aa6SEmmanuel Vadot
99aa7c3aa6SEmmanuel Vadot error = clk_enable(sc->clk);
100aa7c3aa6SEmmanuel Vadot } else
101aa7c3aa6SEmmanuel Vadot error = clk_disable(sc->clk);
102aa7c3aa6SEmmanuel Vadot
103aa7c3aa6SEmmanuel Vadot if (error != 0) {
104aa7c3aa6SEmmanuel Vadot device_printf(dev, "Cannot %sable the clock\n",
105aa7c3aa6SEmmanuel Vadot enable ? "En" : "Dis");
106aa7c3aa6SEmmanuel Vadot goto fail;
107aa7c3aa6SEmmanuel Vadot }
108aa7c3aa6SEmmanuel Vadot }
109aa7c3aa6SEmmanuel Vadot if (sc->vcc_supply) {
110aa7c3aa6SEmmanuel Vadot if (enable)
111aa7c3aa6SEmmanuel Vadot error = regulator_enable(sc->vcc_supply);
112aa7c3aa6SEmmanuel Vadot else
113aa7c3aa6SEmmanuel Vadot error = regulator_disable(sc->vcc_supply);
114aa7c3aa6SEmmanuel Vadot if (error != 0) {
115aa7c3aa6SEmmanuel Vadot device_printf(dev, "Cannot %sable the regulator\n",
116aa7c3aa6SEmmanuel Vadot enable ? "En" : "Dis");
117aa7c3aa6SEmmanuel Vadot goto fail;
118aa7c3aa6SEmmanuel Vadot }
119aa7c3aa6SEmmanuel Vadot }
120aa7c3aa6SEmmanuel Vadot
121aa7c3aa6SEmmanuel Vadot return (0);
122aa7c3aa6SEmmanuel Vadot
123aa7c3aa6SEmmanuel Vadot fail:
124aa7c3aa6SEmmanuel Vadot return (ENXIO);
125aa7c3aa6SEmmanuel Vadot }
126aa7c3aa6SEmmanuel Vadot
127aa7c3aa6SEmmanuel Vadot static int
usb_nop_xceiv_probe(device_t dev)128aa7c3aa6SEmmanuel Vadot usb_nop_xceiv_probe(device_t dev)
129aa7c3aa6SEmmanuel Vadot {
130aa7c3aa6SEmmanuel Vadot
131aa7c3aa6SEmmanuel Vadot if (!ofw_bus_status_okay(dev))
132aa7c3aa6SEmmanuel Vadot return (ENXIO);
133aa7c3aa6SEmmanuel Vadot
134aa7c3aa6SEmmanuel Vadot if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
135aa7c3aa6SEmmanuel Vadot return (ENXIO);
136aa7c3aa6SEmmanuel Vadot
137aa7c3aa6SEmmanuel Vadot device_set_desc(dev, "USB NOP PHY");
138aa7c3aa6SEmmanuel Vadot return (BUS_PROBE_DEFAULT);
139aa7c3aa6SEmmanuel Vadot }
140aa7c3aa6SEmmanuel Vadot
141aa7c3aa6SEmmanuel Vadot static int
usb_nop_xceiv_attach(device_t dev)142aa7c3aa6SEmmanuel Vadot usb_nop_xceiv_attach(device_t dev)
143aa7c3aa6SEmmanuel Vadot {
144aa7c3aa6SEmmanuel Vadot struct usb_nop_xceiv_softc *sc;
145aa7c3aa6SEmmanuel Vadot struct phynode *phynode;
146aa7c3aa6SEmmanuel Vadot struct phynode_init_def phy_init;
147aa7c3aa6SEmmanuel Vadot phandle_t node;
148aa7c3aa6SEmmanuel Vadot int error;
149aa7c3aa6SEmmanuel Vadot
150aa7c3aa6SEmmanuel Vadot sc = device_get_softc(dev);
151aa7c3aa6SEmmanuel Vadot sc->dev = dev;
152aa7c3aa6SEmmanuel Vadot node = ofw_bus_get_node(dev);
153aa7c3aa6SEmmanuel Vadot
154aa7c3aa6SEmmanuel Vadot /* Parse the optional properties */
155aa7c3aa6SEmmanuel Vadot OF_getencprop(node, "clock-frequency", &sc->clk_freq, sizeof(uint32_t));
156aa7c3aa6SEmmanuel Vadot
157aa7c3aa6SEmmanuel Vadot error = clk_get_by_ofw_name(dev, node, "main_clk", &sc->clk);
158aa7c3aa6SEmmanuel Vadot if (error != 0 && sc->clk_freq != 0) {
159aa7c3aa6SEmmanuel Vadot device_printf(dev, "clock property is mandatory if clock-frequency is present\n");
160aa7c3aa6SEmmanuel Vadot return (ENXIO);
161aa7c3aa6SEmmanuel Vadot }
162aa7c3aa6SEmmanuel Vadot
163aa7c3aa6SEmmanuel Vadot regulator_get_by_ofw_property(dev, node, "vcc-supply", &sc->vcc_supply);
164aa7c3aa6SEmmanuel Vadot
165aa7c3aa6SEmmanuel Vadot phy_init.id = 0;
166aa7c3aa6SEmmanuel Vadot phy_init.ofw_node = node;
167aa7c3aa6SEmmanuel Vadot phynode = phynode_create(dev, &usb_nop_xceiv_phynode_class,
168aa7c3aa6SEmmanuel Vadot &phy_init);
169aa7c3aa6SEmmanuel Vadot if (phynode == NULL) {
170aa7c3aa6SEmmanuel Vadot device_printf(dev, "failed to create USB NOP PHY\n");
171aa7c3aa6SEmmanuel Vadot return (ENXIO);
172aa7c3aa6SEmmanuel Vadot }
173aa7c3aa6SEmmanuel Vadot if (phynode_register(phynode) == NULL) {
174aa7c3aa6SEmmanuel Vadot device_printf(dev, "failed to create USB NOP PHY\n");
175aa7c3aa6SEmmanuel Vadot return (ENXIO);
176aa7c3aa6SEmmanuel Vadot }
177aa7c3aa6SEmmanuel Vadot
178aa7c3aa6SEmmanuel Vadot OF_device_register_xref(OF_xref_from_node(node), dev);
179aa7c3aa6SEmmanuel Vadot
180aa7c3aa6SEmmanuel Vadot return (0);
181aa7c3aa6SEmmanuel Vadot }
182aa7c3aa6SEmmanuel Vadot
183aa7c3aa6SEmmanuel Vadot static int
usb_nop_xceiv_detach(device_t dev)184aa7c3aa6SEmmanuel Vadot usb_nop_xceiv_detach(device_t dev)
185aa7c3aa6SEmmanuel Vadot {
186aa7c3aa6SEmmanuel Vadot
187aa7c3aa6SEmmanuel Vadot return (EBUSY);
188aa7c3aa6SEmmanuel Vadot }
189aa7c3aa6SEmmanuel Vadot
190aa7c3aa6SEmmanuel Vadot static device_method_t usb_nop_xceiv_methods[] = {
191aa7c3aa6SEmmanuel Vadot /* Device interface */
192aa7c3aa6SEmmanuel Vadot DEVMETHOD(device_probe, usb_nop_xceiv_probe),
193aa7c3aa6SEmmanuel Vadot DEVMETHOD(device_attach, usb_nop_xceiv_attach),
194aa7c3aa6SEmmanuel Vadot DEVMETHOD(device_detach, usb_nop_xceiv_detach),
195aa7c3aa6SEmmanuel Vadot
196aa7c3aa6SEmmanuel Vadot DEVMETHOD_END
197aa7c3aa6SEmmanuel Vadot };
198aa7c3aa6SEmmanuel Vadot
199aa7c3aa6SEmmanuel Vadot static driver_t usb_nop_xceiv_driver = {
200aa7c3aa6SEmmanuel Vadot "usb_nop_xceiv",
201aa7c3aa6SEmmanuel Vadot usb_nop_xceiv_methods,
202aa7c3aa6SEmmanuel Vadot sizeof(struct usb_nop_xceiv_softc),
203aa7c3aa6SEmmanuel Vadot };
204aa7c3aa6SEmmanuel Vadot
205aa7c3aa6SEmmanuel Vadot EARLY_DRIVER_MODULE(usb_nop_xceiv, simplebus, usb_nop_xceiv_driver,
206bc9372d7SJohn Baldwin 0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
207