Path: blob/main/sys/arm/qualcomm/ipq4018_usb_ss_phy.c
39507 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2019 Michal Meloun <[email protected]>4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND15* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE16* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE17* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE18* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL19* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS20* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)21* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT22* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY23* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF24* SUCH DAMAGE.25*/2627#include <sys/param.h>28#include <sys/systm.h>29#include <sys/bus.h>30#include <sys/gpio.h>31#include <sys/kernel.h>32#include <sys/module.h>33#include <sys/malloc.h>34#include <sys/rman.h>3536#include <machine/bus.h>3738#include <dev/hwreset/hwreset.h>39#include <dev/phy/phy_usb.h>40#include <dev/regulator/regulator.h>41#include <dev/ofw/ofw_bus.h>42#include <dev/ofw/ofw_bus_subr.h>4344#include <dev/fdt/simple_mfd.h>45#include "phynode_if.h"46#include "phynode_usb_if.h"4748static struct ofw_compat_data compat_data[] = {49{"qcom,usb-ss-ipq4019-phy", 1},50{NULL, 0},51};5253struct ipq4018_usb_ss_phy_softc {54device_t dev;55};5657struct ipq4018_usb_ss_phynode_sc {58struct phynode_usb_sc usb_sc;59int mode;60hwreset_t por_rst;61};6263static int64ipq4018_usb_ss_phynode_phy_enable(struct phynode *phynode, bool enable)65{66struct ipq4018_usb_ss_phynode_sc *sc;67device_t dev;68int rv;6970dev = phynode_get_device(phynode);71sc = phynode_get_softc(phynode);7273/*74* For power-off - assert por, sleep for 10ms75*/76rv = hwreset_assert(sc->por_rst);77if (rv != 0)78goto done;79DELAY(10*1000);8081/*82* For power-on - power off first, then deassert por.83*/84if (enable) {85rv = hwreset_deassert(sc->por_rst);86if (rv != 0)87goto done;88DELAY(10*1000);89}9091done:92if (rv != 0) {93device_printf(dev, "%s: failed, rv=%d\n", __func__, rv);94}95return (rv);96}9798/* Phy controller class and methods. */99static phynode_method_t ipq4018_usb_ss_phynode_methods[] = {100PHYNODEUSBMETHOD(phynode_enable, ipq4018_usb_ss_phynode_phy_enable),101PHYNODEUSBMETHOD_END102};103DEFINE_CLASS_1(ipq4018_usb_ss_phynode, ipq4018_usb_ss_phynode_class,104ipq4018_usb_ss_phynode_methods,105sizeof(struct ipq4018_usb_ss_phynode_sc), phynode_usb_class);106107static int108ipq4018_usb_ss_usbphy_init_phy(struct ipq4018_usb_ss_phy_softc *sc,109phandle_t node)110{111struct phynode *phynode;112struct phynode_init_def phy_init;113struct ipq4018_usb_ss_phynode_sc *phy_sc;114int rv;115hwreset_t por_rst = NULL;116117/* FDT resources */118rv = hwreset_get_by_ofw_name(sc->dev, node, "por_rst", &por_rst);119if (rv != 0 && rv != ENOENT) {120device_printf(sc->dev, "Cannot get 'por_rst' reset\n");121goto fail;122}123124/* Create and register phy. */125bzero(&phy_init, sizeof(phy_init));126phy_init.id = 1;127phy_init.ofw_node = node;128phynode = phynode_create(sc->dev, &ipq4018_usb_ss_phynode_class,129&phy_init);130if (phynode == NULL) {131device_printf(sc->dev, "Cannot create phy.\n");132return (ENXIO);133}134135phy_sc = phynode_get_softc(phynode);136phy_sc->por_rst = por_rst;137138if (phynode_register(phynode) == NULL) {139device_printf(sc->dev, "Cannot register phy.\n");140return (ENXIO);141}142143(void) ipq4018_usb_ss_phynode_phy_enable(phynode, true);144145return (0);146147fail:148if (por_rst != NULL)149hwreset_release(por_rst);150151return (ENXIO);152}153154static int155ipq4018_usb_ss_usbphy_probe(device_t dev)156{157158if (!ofw_bus_status_okay(dev))159return (ENXIO);160161if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)162return (ENXIO);163164device_set_desc(dev, "IPQ4018/IPQ4019 USB SS PHY");165return (BUS_PROBE_DEFAULT);166}167168static int169ipq4018_usb_ss_usbphy_attach(device_t dev)170{171struct ipq4018_usb_ss_phy_softc *sc;172phandle_t node;173int rv;174175sc = device_get_softc(dev);176sc->dev = dev;177node = ofw_bus_get_node(sc->dev);178179rv = ipq4018_usb_ss_usbphy_init_phy(sc, node);180if (rv != 0)181goto fail;182bus_attach_children(dev);183return (0);184185fail:186return (ENXIO);187}188189static int190ipq4018_usb_ss_usbphy_detach(device_t dev)191{192193return (0);194}195196static device_method_t ipq4018_usb_ss_usbphy_methods[] = {197/* Device interface */198DEVMETHOD(device_probe, ipq4018_usb_ss_usbphy_probe),199DEVMETHOD(device_attach, ipq4018_usb_ss_usbphy_attach),200DEVMETHOD(device_detach, ipq4018_usb_ss_usbphy_detach),201DEVMETHOD_END202};203204static DEFINE_CLASS_0(ipq4018_usb_ss_usbphy, ipq4018_usb_ss_usbphy_driver,205ipq4018_usb_ss_usbphy_methods,206sizeof(struct ipq4018_usb_ss_phy_softc));207EARLY_DRIVER_MODULE(ipq4018_usb_ss_usbphy, simplebus,208ipq4018_usb_ss_usbphy_driver, NULL, NULL,209BUS_PASS_TIMER + BUS_PASS_ORDER_LAST);210211212