xref: /freebsd/sys/arm/qualcomm/ipq4018_usb_ss_phy.c (revision 18250ec6c089c0c50cbd9fd87d78e03ff89916df)
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