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-hs-ipq4019-phy", 1},
5186f0c3ecSAdrian Chadd {NULL, 0},
5286f0c3ecSAdrian Chadd };
5386f0c3ecSAdrian Chadd
5486f0c3ecSAdrian Chadd struct ipq4018_usb_hs_phy_softc {
5586f0c3ecSAdrian Chadd device_t dev;
5686f0c3ecSAdrian Chadd };
5786f0c3ecSAdrian Chadd
5886f0c3ecSAdrian Chadd struct ipq4018_usb_hs_phynode_sc {
5986f0c3ecSAdrian Chadd struct phynode_usb_sc usb_sc;
6086f0c3ecSAdrian Chadd int mode;
6186f0c3ecSAdrian Chadd hwreset_t por_rst;
6286f0c3ecSAdrian Chadd hwreset_t srif_rst;
6386f0c3ecSAdrian Chadd };
6486f0c3ecSAdrian Chadd
6586f0c3ecSAdrian Chadd static int
ipq4018_usb_hs_phynode_phy_enable(struct phynode * phynode,bool enable)6686f0c3ecSAdrian Chadd ipq4018_usb_hs_phynode_phy_enable(struct phynode *phynode, bool enable)
6786f0c3ecSAdrian Chadd {
6886f0c3ecSAdrian Chadd struct ipq4018_usb_hs_phynode_sc *sc;
6986f0c3ecSAdrian Chadd device_t dev;
7086f0c3ecSAdrian Chadd int rv;
7186f0c3ecSAdrian Chadd
7286f0c3ecSAdrian Chadd dev = phynode_get_device(phynode);
7386f0c3ecSAdrian Chadd sc = phynode_get_softc(phynode);
7486f0c3ecSAdrian Chadd
7586f0c3ecSAdrian Chadd /*
7686f0c3ecSAdrian Chadd *
7786f0c3ecSAdrian Chadd * For power-off - assert por, sleep for 10ms, assert srif,
7886f0c3ecSAdrian Chadd * sleep for 10ms
7986f0c3ecSAdrian Chadd */
8086f0c3ecSAdrian Chadd rv = hwreset_assert(sc->por_rst);
8186f0c3ecSAdrian Chadd if (rv != 0)
8286f0c3ecSAdrian Chadd goto done;
8386f0c3ecSAdrian Chadd DELAY(10*1000);
8486f0c3ecSAdrian Chadd rv = hwreset_assert(sc->srif_rst);
8586f0c3ecSAdrian Chadd if (rv != 0)
8686f0c3ecSAdrian Chadd goto done;
8786f0c3ecSAdrian Chadd DELAY(10*1000);
8886f0c3ecSAdrian Chadd
8986f0c3ecSAdrian Chadd /*
9086f0c3ecSAdrian Chadd * For power-on - power off first, then deassert srif, then
9186f0c3ecSAdrian Chadd * sleep for 10ms, then deassert por.
9286f0c3ecSAdrian Chadd */
9386f0c3ecSAdrian Chadd if (enable) {
9486f0c3ecSAdrian Chadd rv = hwreset_deassert(sc->srif_rst);
9586f0c3ecSAdrian Chadd if (rv != 0)
9686f0c3ecSAdrian Chadd goto done;
9786f0c3ecSAdrian Chadd DELAY(10*1000);
9886f0c3ecSAdrian Chadd rv = hwreset_deassert(sc->por_rst);
9986f0c3ecSAdrian Chadd if (rv != 0)
10086f0c3ecSAdrian Chadd goto done;
10186f0c3ecSAdrian Chadd DELAY(10*1000);
10286f0c3ecSAdrian Chadd }
10386f0c3ecSAdrian Chadd
10486f0c3ecSAdrian Chadd done:
10586f0c3ecSAdrian Chadd if (rv != 0) {
10686f0c3ecSAdrian Chadd device_printf(dev, "%s: failed, rv=%d\n", __func__, rv);
10786f0c3ecSAdrian Chadd }
10886f0c3ecSAdrian Chadd return (rv);
10986f0c3ecSAdrian Chadd }
11086f0c3ecSAdrian Chadd
11186f0c3ecSAdrian Chadd /* Phy controller class and methods. */
11286f0c3ecSAdrian Chadd static phynode_method_t ipq4018_usb_hs_phynode_methods[] = {
11386f0c3ecSAdrian Chadd PHYNODEUSBMETHOD(phynode_enable, ipq4018_usb_hs_phynode_phy_enable),
11486f0c3ecSAdrian Chadd PHYNODEUSBMETHOD_END
11586f0c3ecSAdrian Chadd };
11686f0c3ecSAdrian Chadd DEFINE_CLASS_1(ipq4018_usb_hs_phynode, ipq4018_usb_hs_phynode_class,
11786f0c3ecSAdrian Chadd ipq4018_usb_hs_phynode_methods,
11886f0c3ecSAdrian Chadd sizeof(struct ipq4018_usb_hs_phynode_sc), phynode_usb_class);
11986f0c3ecSAdrian Chadd
12086f0c3ecSAdrian Chadd static int
ipq4018_usb_hs_usbphy_init_phy(struct ipq4018_usb_hs_phy_softc * sc,phandle_t node)12186f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_init_phy(struct ipq4018_usb_hs_phy_softc *sc,
12286f0c3ecSAdrian Chadd phandle_t node)
12386f0c3ecSAdrian Chadd {
12486f0c3ecSAdrian Chadd struct phynode *phynode;
12586f0c3ecSAdrian Chadd struct phynode_init_def phy_init;
12686f0c3ecSAdrian Chadd struct ipq4018_usb_hs_phynode_sc *phy_sc;
12786f0c3ecSAdrian Chadd int rv;
12886f0c3ecSAdrian Chadd hwreset_t por_rst = NULL, srif_rst = NULL;
12986f0c3ecSAdrian Chadd
13086f0c3ecSAdrian Chadd /* FDT resources */
13186f0c3ecSAdrian Chadd rv = hwreset_get_by_ofw_name(sc->dev, node, "por_rst", &por_rst);
13286f0c3ecSAdrian Chadd if (rv != 0 && rv != ENOENT) {
13386f0c3ecSAdrian Chadd device_printf(sc->dev, "Cannot get 'por_rst' reset\n");
13486f0c3ecSAdrian Chadd goto fail;
13586f0c3ecSAdrian Chadd }
13686f0c3ecSAdrian Chadd rv = hwreset_get_by_ofw_name(sc->dev, node, "srif_rst", &srif_rst);
13786f0c3ecSAdrian Chadd if (rv != 0 && rv != ENOENT) {
13886f0c3ecSAdrian Chadd device_printf(sc->dev, "Cannot get 'srif_rst' reset\n");
13986f0c3ecSAdrian Chadd goto fail;
14086f0c3ecSAdrian Chadd }
14186f0c3ecSAdrian Chadd
14286f0c3ecSAdrian Chadd /* Create and register phy. */
14386f0c3ecSAdrian Chadd bzero(&phy_init, sizeof(phy_init));
14486f0c3ecSAdrian Chadd phy_init.id = 1;
14586f0c3ecSAdrian Chadd phy_init.ofw_node = node;
14686f0c3ecSAdrian Chadd phynode = phynode_create(sc->dev, &ipq4018_usb_hs_phynode_class,
14786f0c3ecSAdrian Chadd &phy_init);
14886f0c3ecSAdrian Chadd if (phynode == NULL) {
14986f0c3ecSAdrian Chadd device_printf(sc->dev, "Cannot create phy.\n");
15086f0c3ecSAdrian Chadd return (ENXIO);
15186f0c3ecSAdrian Chadd }
15286f0c3ecSAdrian Chadd
15386f0c3ecSAdrian Chadd phy_sc = phynode_get_softc(phynode);
15486f0c3ecSAdrian Chadd phy_sc->por_rst = por_rst;
15586f0c3ecSAdrian Chadd phy_sc->srif_rst = srif_rst;
15686f0c3ecSAdrian Chadd
15786f0c3ecSAdrian Chadd if (phynode_register(phynode) == NULL) {
15886f0c3ecSAdrian Chadd device_printf(sc->dev, "Cannot register phy.\n");
15986f0c3ecSAdrian Chadd return (ENXIO);
16086f0c3ecSAdrian Chadd }
16186f0c3ecSAdrian Chadd
16286f0c3ecSAdrian Chadd (void) ipq4018_usb_hs_phynode_phy_enable(phynode, true);
16386f0c3ecSAdrian Chadd
16486f0c3ecSAdrian Chadd return (0);
16586f0c3ecSAdrian Chadd
16686f0c3ecSAdrian Chadd fail:
16786f0c3ecSAdrian Chadd if (por_rst != NULL)
16886f0c3ecSAdrian Chadd hwreset_release(por_rst);
16986f0c3ecSAdrian Chadd if (srif_rst != NULL)
17086f0c3ecSAdrian Chadd hwreset_release(srif_rst);
17186f0c3ecSAdrian Chadd
17286f0c3ecSAdrian Chadd return (ENXIO);
17386f0c3ecSAdrian Chadd }
17486f0c3ecSAdrian Chadd
17586f0c3ecSAdrian Chadd static int
ipq4018_usb_hs_usbphy_probe(device_t dev)17686f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_probe(device_t dev)
17786f0c3ecSAdrian Chadd {
17886f0c3ecSAdrian Chadd
17986f0c3ecSAdrian Chadd if (!ofw_bus_status_okay(dev))
18086f0c3ecSAdrian Chadd return (ENXIO);
18186f0c3ecSAdrian Chadd
18286f0c3ecSAdrian Chadd if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
18386f0c3ecSAdrian Chadd return (ENXIO);
18486f0c3ecSAdrian Chadd
18586f0c3ecSAdrian Chadd device_set_desc(dev, "IPQ4018/IPQ4019 USB HS PHY");
18686f0c3ecSAdrian Chadd return (BUS_PROBE_DEFAULT);
18786f0c3ecSAdrian Chadd }
18886f0c3ecSAdrian Chadd
18986f0c3ecSAdrian Chadd static int
ipq4018_usb_hs_usbphy_attach(device_t dev)19086f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_attach(device_t dev)
19186f0c3ecSAdrian Chadd {
19286f0c3ecSAdrian Chadd struct ipq4018_usb_hs_phy_softc *sc;
19386f0c3ecSAdrian Chadd phandle_t node;
19486f0c3ecSAdrian Chadd int rv;
19586f0c3ecSAdrian Chadd
19686f0c3ecSAdrian Chadd sc = device_get_softc(dev);
19786f0c3ecSAdrian Chadd sc->dev = dev;
19886f0c3ecSAdrian Chadd node = ofw_bus_get_node(sc->dev);
19986f0c3ecSAdrian Chadd
20086f0c3ecSAdrian Chadd rv = ipq4018_usb_hs_usbphy_init_phy(sc, node);
20186f0c3ecSAdrian Chadd if (rv != 0)
20286f0c3ecSAdrian Chadd goto fail;
203*18250ec6SJohn Baldwin bus_attach_children(dev);
204*18250ec6SJohn Baldwin return (0);
20586f0c3ecSAdrian Chadd
20686f0c3ecSAdrian Chadd fail:
20786f0c3ecSAdrian Chadd return (ENXIO);
20886f0c3ecSAdrian Chadd }
20986f0c3ecSAdrian Chadd
21086f0c3ecSAdrian Chadd static int
ipq4018_usb_hs_usbphy_detach(device_t dev)21186f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_detach(device_t dev)
21286f0c3ecSAdrian Chadd {
21386f0c3ecSAdrian Chadd
21486f0c3ecSAdrian Chadd return (0);
21586f0c3ecSAdrian Chadd }
21686f0c3ecSAdrian Chadd
21786f0c3ecSAdrian Chadd static device_method_t ipq4018_usb_hs_usbphy_methods[] = {
21886f0c3ecSAdrian Chadd /* Device interface */
21986f0c3ecSAdrian Chadd DEVMETHOD(device_probe, ipq4018_usb_hs_usbphy_probe),
22086f0c3ecSAdrian Chadd DEVMETHOD(device_attach, ipq4018_usb_hs_usbphy_attach),
22186f0c3ecSAdrian Chadd DEVMETHOD(device_detach, ipq4018_usb_hs_usbphy_detach),
22286f0c3ecSAdrian Chadd DEVMETHOD_END
22386f0c3ecSAdrian Chadd };
22486f0c3ecSAdrian Chadd
22586f0c3ecSAdrian Chadd static DEFINE_CLASS_0(ipq4018_usb_hs_usbphy, ipq4018_usb_hs_usbphy_driver,
22686f0c3ecSAdrian Chadd ipq4018_usb_hs_usbphy_methods,
22786f0c3ecSAdrian Chadd sizeof(struct ipq4018_usb_hs_phy_softc));
22886f0c3ecSAdrian Chadd EARLY_DRIVER_MODULE(ipq4018_usb_hs_usbphy, simplebus,
2299978ade4SJohn Baldwin ipq4018_usb_hs_usbphy_driver, NULL, NULL,
23086f0c3ecSAdrian Chadd BUS_PASS_TIMER + BUS_PASS_ORDER_LAST);
231