xref: /freebsd/sys/arm/qualcomm/ipq4018_usb_ss_phy.c (revision 9978ade4980452e3699285c01a4b9c4b5adc68e4)
186f0c3ecSAdrian Chadd /*-
286f0c3ecSAdrian Chadd  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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/cdefs.h>
2986f0c3ecSAdrian Chadd __FBSDID("$FreeBSD$");
3086f0c3ecSAdrian Chadd 
3186f0c3ecSAdrian Chadd #include <sys/param.h>
3286f0c3ecSAdrian Chadd #include <sys/systm.h>
3386f0c3ecSAdrian Chadd #include <sys/bus.h>
3486f0c3ecSAdrian Chadd #include <sys/gpio.h>
3586f0c3ecSAdrian Chadd #include <sys/kernel.h>
3686f0c3ecSAdrian Chadd #include <sys/module.h>
3786f0c3ecSAdrian Chadd #include <sys/malloc.h>
3886f0c3ecSAdrian Chadd #include <sys/rman.h>
3986f0c3ecSAdrian Chadd 
4086f0c3ecSAdrian Chadd #include <machine/bus.h>
4186f0c3ecSAdrian Chadd 
4286f0c3ecSAdrian Chadd #include <dev/extres/hwreset/hwreset.h>
4386f0c3ecSAdrian Chadd #include <dev/extres/phy/phy_usb.h>
4486f0c3ecSAdrian Chadd #include <dev/extres/regulator/regulator.h>
4586f0c3ecSAdrian Chadd #include <dev/ofw/ofw_bus.h>
4686f0c3ecSAdrian Chadd #include <dev/ofw/ofw_bus_subr.h>
4786f0c3ecSAdrian Chadd 
4886f0c3ecSAdrian Chadd #include <dev/fdt/simple_mfd.h>
4986f0c3ecSAdrian Chadd #include "phynode_if.h"
5086f0c3ecSAdrian Chadd #include "phynode_usb_if.h"
5186f0c3ecSAdrian Chadd 
5286f0c3ecSAdrian Chadd static struct ofw_compat_data compat_data[] = {
5386f0c3ecSAdrian Chadd 	{"qcom,usb-ss-ipq4019-phy",	1},
5486f0c3ecSAdrian Chadd 	{NULL,				0},
5586f0c3ecSAdrian Chadd };
5686f0c3ecSAdrian Chadd 
5786f0c3ecSAdrian Chadd struct ipq4018_usb_ss_phy_softc {
5886f0c3ecSAdrian Chadd 	device_t		dev;
5986f0c3ecSAdrian Chadd };
6086f0c3ecSAdrian Chadd 
6186f0c3ecSAdrian Chadd struct ipq4018_usb_ss_phynode_sc {
6286f0c3ecSAdrian Chadd 	struct phynode_usb_sc	usb_sc;
6386f0c3ecSAdrian Chadd 	int			mode;
6486f0c3ecSAdrian Chadd 	hwreset_t		por_rst;
6586f0c3ecSAdrian Chadd };
6686f0c3ecSAdrian Chadd 
6786f0c3ecSAdrian Chadd static int
6886f0c3ecSAdrian Chadd ipq4018_usb_ss_phynode_phy_enable(struct phynode *phynode, bool enable)
6986f0c3ecSAdrian Chadd {
7086f0c3ecSAdrian Chadd 	struct ipq4018_usb_ss_phynode_sc *sc;
7186f0c3ecSAdrian Chadd 	device_t dev;
7286f0c3ecSAdrian Chadd 	int rv;
7386f0c3ecSAdrian Chadd 
7486f0c3ecSAdrian Chadd 	dev = phynode_get_device(phynode);
7586f0c3ecSAdrian Chadd 	sc = phynode_get_softc(phynode);
7686f0c3ecSAdrian Chadd 
7786f0c3ecSAdrian Chadd 	/*
7886f0c3ecSAdrian Chadd 	 * For power-off - assert por, 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 
8586f0c3ecSAdrian Chadd 	/*
8686f0c3ecSAdrian Chadd 	 * For power-on - power off first, then deassert por.
8786f0c3ecSAdrian Chadd 	 */
8886f0c3ecSAdrian Chadd 	if (enable) {
8986f0c3ecSAdrian Chadd 		rv = hwreset_deassert(sc->por_rst);
9086f0c3ecSAdrian Chadd 		if (rv != 0)
9186f0c3ecSAdrian Chadd 			goto done;
9286f0c3ecSAdrian Chadd 		DELAY(10*1000);
9386f0c3ecSAdrian Chadd 	}
9486f0c3ecSAdrian Chadd 
9586f0c3ecSAdrian Chadd done:
9686f0c3ecSAdrian Chadd 	if (rv != 0) {
9786f0c3ecSAdrian Chadd 		device_printf(dev, "%s: failed, rv=%d\n", __func__, rv);
9886f0c3ecSAdrian Chadd 	}
9986f0c3ecSAdrian Chadd 	return (rv);
10086f0c3ecSAdrian Chadd }
10186f0c3ecSAdrian Chadd 
10286f0c3ecSAdrian Chadd  /* Phy controller class and methods. */
10386f0c3ecSAdrian Chadd static phynode_method_t ipq4018_usb_ss_phynode_methods[] = {
10486f0c3ecSAdrian Chadd 	PHYNODEUSBMETHOD(phynode_enable,	ipq4018_usb_ss_phynode_phy_enable),
10586f0c3ecSAdrian Chadd 	PHYNODEUSBMETHOD_END
10686f0c3ecSAdrian Chadd };
10786f0c3ecSAdrian Chadd DEFINE_CLASS_1(ipq4018_usb_ss_phynode, ipq4018_usb_ss_phynode_class,
10886f0c3ecSAdrian Chadd     ipq4018_usb_ss_phynode_methods,
10986f0c3ecSAdrian Chadd     sizeof(struct ipq4018_usb_ss_phynode_sc), phynode_usb_class);
11086f0c3ecSAdrian Chadd 
11186f0c3ecSAdrian Chadd static int
11286f0c3ecSAdrian Chadd ipq4018_usb_ss_usbphy_init_phy(struct ipq4018_usb_ss_phy_softc *sc,
11386f0c3ecSAdrian Chadd     phandle_t node)
11486f0c3ecSAdrian Chadd {
11586f0c3ecSAdrian Chadd 	struct phynode *phynode;
11686f0c3ecSAdrian Chadd 	struct phynode_init_def phy_init;
11786f0c3ecSAdrian Chadd 	struct ipq4018_usb_ss_phynode_sc *phy_sc;
11886f0c3ecSAdrian Chadd 	int rv;
11986f0c3ecSAdrian Chadd 	hwreset_t por_rst = NULL;
12086f0c3ecSAdrian Chadd 
12186f0c3ecSAdrian Chadd 	/* FDT resources */
12286f0c3ecSAdrian Chadd 	rv = hwreset_get_by_ofw_name(sc->dev, node, "por_rst", &por_rst);
12386f0c3ecSAdrian Chadd 	if (rv != 0 && rv != ENOENT) {
12486f0c3ecSAdrian Chadd 		device_printf(sc->dev, "Cannot get 'por_rst' reset\n");
12586f0c3ecSAdrian Chadd 		goto fail;
12686f0c3ecSAdrian Chadd 	}
12786f0c3ecSAdrian Chadd 
12886f0c3ecSAdrian Chadd 	/* Create and register phy. */
12986f0c3ecSAdrian Chadd 	bzero(&phy_init, sizeof(phy_init));
13086f0c3ecSAdrian Chadd 	phy_init.id = 1;
13186f0c3ecSAdrian Chadd 	phy_init.ofw_node = node;
13286f0c3ecSAdrian Chadd 	phynode = phynode_create(sc->dev, &ipq4018_usb_ss_phynode_class,
13386f0c3ecSAdrian Chadd 	    &phy_init);
13486f0c3ecSAdrian Chadd 	if (phynode == NULL) {
13586f0c3ecSAdrian Chadd 		device_printf(sc->dev, "Cannot create phy.\n");
13686f0c3ecSAdrian Chadd 		return (ENXIO);
13786f0c3ecSAdrian Chadd 	}
13886f0c3ecSAdrian Chadd 
13986f0c3ecSAdrian Chadd 	phy_sc = phynode_get_softc(phynode);
14086f0c3ecSAdrian Chadd 	phy_sc->por_rst = por_rst;
14186f0c3ecSAdrian Chadd 
14286f0c3ecSAdrian Chadd 	if (phynode_register(phynode) == NULL) {
14386f0c3ecSAdrian Chadd 		device_printf(sc->dev, "Cannot register phy.\n");
14486f0c3ecSAdrian Chadd 		return (ENXIO);
14586f0c3ecSAdrian Chadd 	}
14686f0c3ecSAdrian Chadd 
14786f0c3ecSAdrian Chadd 	(void) ipq4018_usb_ss_phynode_phy_enable(phynode, true);
14886f0c3ecSAdrian Chadd 
14986f0c3ecSAdrian Chadd 	return (0);
15086f0c3ecSAdrian Chadd 
15186f0c3ecSAdrian Chadd fail:
15286f0c3ecSAdrian Chadd 	if (por_rst != NULL)
15386f0c3ecSAdrian Chadd 		 hwreset_release(por_rst);
15486f0c3ecSAdrian Chadd 
15586f0c3ecSAdrian Chadd 	return (ENXIO);
15686f0c3ecSAdrian Chadd }
15786f0c3ecSAdrian Chadd 
15886f0c3ecSAdrian Chadd static int
15986f0c3ecSAdrian Chadd ipq4018_usb_ss_usbphy_probe(device_t dev)
16086f0c3ecSAdrian Chadd {
16186f0c3ecSAdrian Chadd 
16286f0c3ecSAdrian Chadd 	if (!ofw_bus_status_okay(dev))
16386f0c3ecSAdrian Chadd 		return (ENXIO);
16486f0c3ecSAdrian Chadd 
16586f0c3ecSAdrian Chadd 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
16686f0c3ecSAdrian Chadd 		return (ENXIO);
16786f0c3ecSAdrian Chadd 
16886f0c3ecSAdrian Chadd 	device_set_desc(dev, "IPQ4018/IPQ4019 USB SS PHY");
16986f0c3ecSAdrian Chadd 	return (BUS_PROBE_DEFAULT);
17086f0c3ecSAdrian Chadd }
17186f0c3ecSAdrian Chadd 
17286f0c3ecSAdrian Chadd static int
17386f0c3ecSAdrian Chadd ipq4018_usb_ss_usbphy_attach(device_t dev)
17486f0c3ecSAdrian Chadd {
17586f0c3ecSAdrian Chadd 	struct ipq4018_usb_ss_phy_softc *sc;
17686f0c3ecSAdrian Chadd 	phandle_t node;
17786f0c3ecSAdrian Chadd 	int rv;
17886f0c3ecSAdrian Chadd 
17986f0c3ecSAdrian Chadd 	sc = device_get_softc(dev);
18086f0c3ecSAdrian Chadd 	sc->dev = dev;
18186f0c3ecSAdrian Chadd 	node = ofw_bus_get_node(sc->dev);
18286f0c3ecSAdrian Chadd 
18386f0c3ecSAdrian Chadd 	rv = ipq4018_usb_ss_usbphy_init_phy(sc, node);
18486f0c3ecSAdrian Chadd 	if (rv != 0)
18586f0c3ecSAdrian Chadd 		goto fail;
18686f0c3ecSAdrian Chadd 	return (bus_generic_attach(dev));
18786f0c3ecSAdrian Chadd 
18886f0c3ecSAdrian Chadd fail:
18986f0c3ecSAdrian Chadd 	return (ENXIO);
19086f0c3ecSAdrian Chadd }
19186f0c3ecSAdrian Chadd 
19286f0c3ecSAdrian Chadd static int
19386f0c3ecSAdrian Chadd ipq4018_usb_ss_usbphy_detach(device_t dev)
19486f0c3ecSAdrian Chadd {
19586f0c3ecSAdrian Chadd 	struct ipq4018_usb_ss_phy_softc *sc;
19686f0c3ecSAdrian Chadd 	sc = device_get_softc(dev);
19786f0c3ecSAdrian Chadd 
19886f0c3ecSAdrian Chadd 	return (0);
19986f0c3ecSAdrian Chadd }
20086f0c3ecSAdrian Chadd 
20186f0c3ecSAdrian Chadd static device_method_t ipq4018_usb_ss_usbphy_methods[] = {
20286f0c3ecSAdrian Chadd 	/* Device interface */
20386f0c3ecSAdrian Chadd 	DEVMETHOD(device_probe,			ipq4018_usb_ss_usbphy_probe),
20486f0c3ecSAdrian Chadd 	DEVMETHOD(device_attach,		ipq4018_usb_ss_usbphy_attach),
20586f0c3ecSAdrian Chadd 	DEVMETHOD(device_detach,		ipq4018_usb_ss_usbphy_detach),
20686f0c3ecSAdrian Chadd 	DEVMETHOD_END
20786f0c3ecSAdrian Chadd };
20886f0c3ecSAdrian Chadd 
20986f0c3ecSAdrian Chadd static DEFINE_CLASS_0(ipq4018_usb_ss_usbphy, ipq4018_usb_ss_usbphy_driver,
21086f0c3ecSAdrian Chadd     ipq4018_usb_ss_usbphy_methods,
21186f0c3ecSAdrian Chadd     sizeof(struct ipq4018_usb_ss_phy_softc));
21286f0c3ecSAdrian Chadd EARLY_DRIVER_MODULE(ipq4018_usb_ss_usbphy, simplebus,
213*9978ade4SJohn Baldwin     ipq4018_usb_ss_usbphy_driver, NULL, NULL,
21486f0c3ecSAdrian Chadd     BUS_PASS_TIMER + BUS_PASS_ORDER_LAST);
215