186f0c3ecSAdrian Chadd /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
386f0c3ecSAdrian Chadd *
486f0c3ecSAdrian Chadd * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org>
586f0c3ecSAdrian Chadd *
686f0c3ecSAdrian Chadd * Redistribution and use in source and binary forms, with or without
786f0c3ecSAdrian Chadd * modification, are permitted provided that the following conditions
886f0c3ecSAdrian Chadd * are met:
986f0c3ecSAdrian Chadd * 1. Redistributions of source code must retain the above copyright
1086f0c3ecSAdrian Chadd * notice, this list of conditions and the following disclaimer.
1186f0c3ecSAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright
1286f0c3ecSAdrian Chadd * notice, this list of conditions and the following disclaimer in the
1386f0c3ecSAdrian Chadd * documentation and/or other materials provided with the distribution.
1486f0c3ecSAdrian Chadd *
1586f0c3ecSAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1686f0c3ecSAdrian Chadd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1786f0c3ecSAdrian Chadd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1886f0c3ecSAdrian Chadd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1986f0c3ecSAdrian Chadd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2086f0c3ecSAdrian Chadd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2186f0c3ecSAdrian Chadd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2286f0c3ecSAdrian Chadd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2386f0c3ecSAdrian Chadd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2486f0c3ecSAdrian Chadd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2586f0c3ecSAdrian Chadd * SUCH DAMAGE.
2686f0c3ecSAdrian Chadd */
2786f0c3ecSAdrian Chadd
2886f0c3ecSAdrian Chadd #include <sys/param.h>
2986f0c3ecSAdrian Chadd #include <sys/systm.h>
3086f0c3ecSAdrian Chadd #include <sys/bus.h>
3186f0c3ecSAdrian Chadd #include <sys/gpio.h>
3286f0c3ecSAdrian Chadd #include <sys/kernel.h>
3386f0c3ecSAdrian Chadd #include <sys/module.h>
3486f0c3ecSAdrian Chadd #include <sys/malloc.h>
3586f0c3ecSAdrian Chadd #include <sys/rman.h>
3686f0c3ecSAdrian Chadd
3786f0c3ecSAdrian Chadd #include <machine/bus.h>
3886f0c3ecSAdrian Chadd
391f469a9fSEmmanuel Vadot #include <dev/hwreset/hwreset.h>
40950a6087SEmmanuel Vadot #include <dev/phy/phy_usb.h>
41b2f0caf1SEmmanuel Vadot #include <dev/regulator/regulator.h>
4286f0c3ecSAdrian Chadd #include <dev/ofw/ofw_bus.h>
4386f0c3ecSAdrian Chadd #include <dev/ofw/ofw_bus_subr.h>
4486f0c3ecSAdrian Chadd
4586f0c3ecSAdrian Chadd #include <dev/fdt/simple_mfd.h>
4686f0c3ecSAdrian Chadd #include "phynode_if.h"
4786f0c3ecSAdrian Chadd #include "phynode_usb_if.h"
4886f0c3ecSAdrian Chadd
4986f0c3ecSAdrian Chadd static struct ofw_compat_data compat_data[] = {
5086f0c3ecSAdrian Chadd {"qcom,usb-ss-ipq4019-phy", 1},
5186f0c3ecSAdrian Chadd {NULL, 0},
5286f0c3ecSAdrian Chadd };
5386f0c3ecSAdrian Chadd
5486f0c3ecSAdrian Chadd struct ipq4018_usb_ss_phy_softc {
5586f0c3ecSAdrian Chadd device_t dev;
5686f0c3ecSAdrian Chadd };
5786f0c3ecSAdrian Chadd
5886f0c3ecSAdrian Chadd struct ipq4018_usb_ss_phynode_sc {
5986f0c3ecSAdrian Chadd struct phynode_usb_sc usb_sc;
6086f0c3ecSAdrian Chadd int mode;
6186f0c3ecSAdrian Chadd hwreset_t por_rst;
6286f0c3ecSAdrian Chadd };
6386f0c3ecSAdrian Chadd
6486f0c3ecSAdrian Chadd static int
ipq4018_usb_ss_phynode_phy_enable(struct phynode * phynode,bool enable)6586f0c3ecSAdrian Chadd ipq4018_usb_ss_phynode_phy_enable(struct phynode *phynode, bool enable)
6686f0c3ecSAdrian Chadd {
6786f0c3ecSAdrian Chadd struct ipq4018_usb_ss_phynode_sc *sc;
6886f0c3ecSAdrian Chadd device_t dev;
6986f0c3ecSAdrian Chadd int rv;
7086f0c3ecSAdrian Chadd
7186f0c3ecSAdrian Chadd dev = phynode_get_device(phynode);
7286f0c3ecSAdrian Chadd sc = phynode_get_softc(phynode);
7386f0c3ecSAdrian Chadd
7486f0c3ecSAdrian Chadd /*
7586f0c3ecSAdrian Chadd * For power-off - assert por, sleep for 10ms
7686f0c3ecSAdrian Chadd */
7786f0c3ecSAdrian Chadd rv = hwreset_assert(sc->por_rst);
7886f0c3ecSAdrian Chadd if (rv != 0)
7986f0c3ecSAdrian Chadd goto done;
8086f0c3ecSAdrian Chadd DELAY(10*1000);
8186f0c3ecSAdrian Chadd
8286f0c3ecSAdrian Chadd /*
8386f0c3ecSAdrian Chadd * For power-on - power off first, then deassert por.
8486f0c3ecSAdrian Chadd */
8586f0c3ecSAdrian Chadd if (enable) {
8686f0c3ecSAdrian Chadd rv = hwreset_deassert(sc->por_rst);
8786f0c3ecSAdrian Chadd if (rv != 0)
8886f0c3ecSAdrian Chadd goto done;
8986f0c3ecSAdrian Chadd DELAY(10*1000);
9086f0c3ecSAdrian Chadd }
9186f0c3ecSAdrian Chadd
9286f0c3ecSAdrian Chadd done:
9386f0c3ecSAdrian Chadd if (rv != 0) {
9486f0c3ecSAdrian Chadd device_printf(dev, "%s: failed, rv=%d\n", __func__, rv);
9586f0c3ecSAdrian Chadd }
9686f0c3ecSAdrian Chadd return (rv);
9786f0c3ecSAdrian Chadd }
9886f0c3ecSAdrian Chadd
9986f0c3ecSAdrian Chadd /* Phy controller class and methods. */
10086f0c3ecSAdrian Chadd static phynode_method_t ipq4018_usb_ss_phynode_methods[] = {
10186f0c3ecSAdrian Chadd PHYNODEUSBMETHOD(phynode_enable, ipq4018_usb_ss_phynode_phy_enable),
10286f0c3ecSAdrian Chadd PHYNODEUSBMETHOD_END
10386f0c3ecSAdrian Chadd };
10486f0c3ecSAdrian Chadd DEFINE_CLASS_1(ipq4018_usb_ss_phynode, ipq4018_usb_ss_phynode_class,
10586f0c3ecSAdrian Chadd ipq4018_usb_ss_phynode_methods,
10686f0c3ecSAdrian Chadd sizeof(struct ipq4018_usb_ss_phynode_sc), phynode_usb_class);
10786f0c3ecSAdrian Chadd
10886f0c3ecSAdrian Chadd static int
ipq4018_usb_ss_usbphy_init_phy(struct ipq4018_usb_ss_phy_softc * sc,phandle_t node)10986f0c3ecSAdrian Chadd ipq4018_usb_ss_usbphy_init_phy(struct ipq4018_usb_ss_phy_softc *sc,
11086f0c3ecSAdrian Chadd phandle_t node)
11186f0c3ecSAdrian Chadd {
11286f0c3ecSAdrian Chadd struct phynode *phynode;
11386f0c3ecSAdrian Chadd struct phynode_init_def phy_init;
11486f0c3ecSAdrian Chadd struct ipq4018_usb_ss_phynode_sc *phy_sc;
11586f0c3ecSAdrian Chadd int rv;
11686f0c3ecSAdrian Chadd hwreset_t por_rst = NULL;
11786f0c3ecSAdrian Chadd
11886f0c3ecSAdrian Chadd /* FDT resources */
11986f0c3ecSAdrian Chadd rv = hwreset_get_by_ofw_name(sc->dev, node, "por_rst", &por_rst);
12086f0c3ecSAdrian Chadd if (rv != 0 && rv != ENOENT) {
12186f0c3ecSAdrian Chadd device_printf(sc->dev, "Cannot get 'por_rst' reset\n");
12286f0c3ecSAdrian Chadd goto fail;
12386f0c3ecSAdrian Chadd }
12486f0c3ecSAdrian Chadd
12586f0c3ecSAdrian Chadd /* Create and register phy. */
12686f0c3ecSAdrian Chadd bzero(&phy_init, sizeof(phy_init));
12786f0c3ecSAdrian Chadd phy_init.id = 1;
12886f0c3ecSAdrian Chadd phy_init.ofw_node = node;
12986f0c3ecSAdrian Chadd phynode = phynode_create(sc->dev, &ipq4018_usb_ss_phynode_class,
13086f0c3ecSAdrian Chadd &phy_init);
13186f0c3ecSAdrian Chadd if (phynode == NULL) {
13286f0c3ecSAdrian Chadd device_printf(sc->dev, "Cannot create phy.\n");
13386f0c3ecSAdrian Chadd return (ENXIO);
13486f0c3ecSAdrian Chadd }
13586f0c3ecSAdrian Chadd
13686f0c3ecSAdrian Chadd phy_sc = phynode_get_softc(phynode);
13786f0c3ecSAdrian Chadd phy_sc->por_rst = por_rst;
13886f0c3ecSAdrian Chadd
13986f0c3ecSAdrian Chadd if (phynode_register(phynode) == NULL) {
14086f0c3ecSAdrian Chadd device_printf(sc->dev, "Cannot register phy.\n");
14186f0c3ecSAdrian Chadd return (ENXIO);
14286f0c3ecSAdrian Chadd }
14386f0c3ecSAdrian Chadd
14486f0c3ecSAdrian Chadd (void) ipq4018_usb_ss_phynode_phy_enable(phynode, true);
14586f0c3ecSAdrian Chadd
14686f0c3ecSAdrian Chadd return (0);
14786f0c3ecSAdrian Chadd
14886f0c3ecSAdrian Chadd fail:
14986f0c3ecSAdrian Chadd if (por_rst != NULL)
15086f0c3ecSAdrian Chadd hwreset_release(por_rst);
15186f0c3ecSAdrian Chadd
15286f0c3ecSAdrian Chadd return (ENXIO);
15386f0c3ecSAdrian Chadd }
15486f0c3ecSAdrian Chadd
15586f0c3ecSAdrian Chadd static int
ipq4018_usb_ss_usbphy_probe(device_t dev)15686f0c3ecSAdrian Chadd ipq4018_usb_ss_usbphy_probe(device_t dev)
15786f0c3ecSAdrian Chadd {
15886f0c3ecSAdrian Chadd
15986f0c3ecSAdrian Chadd if (!ofw_bus_status_okay(dev))
16086f0c3ecSAdrian Chadd return (ENXIO);
16186f0c3ecSAdrian Chadd
16286f0c3ecSAdrian Chadd if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
16386f0c3ecSAdrian Chadd return (ENXIO);
16486f0c3ecSAdrian Chadd
16586f0c3ecSAdrian Chadd device_set_desc(dev, "IPQ4018/IPQ4019 USB SS PHY");
16686f0c3ecSAdrian Chadd return (BUS_PROBE_DEFAULT);
16786f0c3ecSAdrian Chadd }
16886f0c3ecSAdrian Chadd
16986f0c3ecSAdrian Chadd static int
ipq4018_usb_ss_usbphy_attach(device_t dev)17086f0c3ecSAdrian Chadd ipq4018_usb_ss_usbphy_attach(device_t dev)
17186f0c3ecSAdrian Chadd {
17286f0c3ecSAdrian Chadd struct ipq4018_usb_ss_phy_softc *sc;
17386f0c3ecSAdrian Chadd phandle_t node;
17486f0c3ecSAdrian Chadd int rv;
17586f0c3ecSAdrian Chadd
17686f0c3ecSAdrian Chadd sc = device_get_softc(dev);
17786f0c3ecSAdrian Chadd sc->dev = dev;
17886f0c3ecSAdrian Chadd node = ofw_bus_get_node(sc->dev);
17986f0c3ecSAdrian Chadd
18086f0c3ecSAdrian Chadd rv = ipq4018_usb_ss_usbphy_init_phy(sc, node);
18186f0c3ecSAdrian Chadd if (rv != 0)
18286f0c3ecSAdrian Chadd goto fail;
183*18250ec6SJohn Baldwin bus_attach_children(dev);
184*18250ec6SJohn Baldwin return (0);
18586f0c3ecSAdrian Chadd
18686f0c3ecSAdrian Chadd fail:
18786f0c3ecSAdrian Chadd return (ENXIO);
18886f0c3ecSAdrian Chadd }
18986f0c3ecSAdrian Chadd
19086f0c3ecSAdrian Chadd static int
ipq4018_usb_ss_usbphy_detach(device_t dev)19186f0c3ecSAdrian Chadd ipq4018_usb_ss_usbphy_detach(device_t dev)
19286f0c3ecSAdrian Chadd {
19386f0c3ecSAdrian Chadd
19486f0c3ecSAdrian Chadd return (0);
19586f0c3ecSAdrian Chadd }
19686f0c3ecSAdrian Chadd
19786f0c3ecSAdrian Chadd static device_method_t ipq4018_usb_ss_usbphy_methods[] = {
19886f0c3ecSAdrian Chadd /* Device interface */
19986f0c3ecSAdrian Chadd DEVMETHOD(device_probe, ipq4018_usb_ss_usbphy_probe),
20086f0c3ecSAdrian Chadd DEVMETHOD(device_attach, ipq4018_usb_ss_usbphy_attach),
20186f0c3ecSAdrian Chadd DEVMETHOD(device_detach, ipq4018_usb_ss_usbphy_detach),
20286f0c3ecSAdrian Chadd DEVMETHOD_END
20386f0c3ecSAdrian Chadd };
20486f0c3ecSAdrian Chadd
20586f0c3ecSAdrian Chadd static DEFINE_CLASS_0(ipq4018_usb_ss_usbphy, ipq4018_usb_ss_usbphy_driver,
20686f0c3ecSAdrian Chadd ipq4018_usb_ss_usbphy_methods,
20786f0c3ecSAdrian Chadd sizeof(struct ipq4018_usb_ss_phy_softc));
20886f0c3ecSAdrian Chadd EARLY_DRIVER_MODULE(ipq4018_usb_ss_usbphy, simplebus,
2099978ade4SJohn Baldwin ipq4018_usb_ss_usbphy_driver, NULL, NULL,
21086f0c3ecSAdrian Chadd BUS_PASS_TIMER + BUS_PASS_ORDER_LAST);
211