Path: blob/main/sys/arm/qualcomm/ipq4018_usb_hs_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-hs-ipq4019-phy", 1},50{NULL, 0},51};5253struct ipq4018_usb_hs_phy_softc {54device_t dev;55};5657struct ipq4018_usb_hs_phynode_sc {58struct phynode_usb_sc usb_sc;59int mode;60hwreset_t por_rst;61hwreset_t srif_rst;62};6364static int65ipq4018_usb_hs_phynode_phy_enable(struct phynode *phynode, bool enable)66{67struct ipq4018_usb_hs_phynode_sc *sc;68device_t dev;69int rv;7071dev = phynode_get_device(phynode);72sc = phynode_get_softc(phynode);7374/*75*76* For power-off - assert por, sleep for 10ms, assert srif,77* sleep for 10ms78*/79rv = hwreset_assert(sc->por_rst);80if (rv != 0)81goto done;82DELAY(10*1000);83rv = hwreset_assert(sc->srif_rst);84if (rv != 0)85goto done;86DELAY(10*1000);8788/*89* For power-on - power off first, then deassert srif, then90* sleep for 10ms, then deassert por.91*/92if (enable) {93rv = hwreset_deassert(sc->srif_rst);94if (rv != 0)95goto done;96DELAY(10*1000);97rv = hwreset_deassert(sc->por_rst);98if (rv != 0)99goto done;100DELAY(10*1000);101}102103done:104if (rv != 0) {105device_printf(dev, "%s: failed, rv=%d\n", __func__, rv);106}107return (rv);108}109110/* Phy controller class and methods. */111static phynode_method_t ipq4018_usb_hs_phynode_methods[] = {112PHYNODEUSBMETHOD(phynode_enable, ipq4018_usb_hs_phynode_phy_enable),113PHYNODEUSBMETHOD_END114};115DEFINE_CLASS_1(ipq4018_usb_hs_phynode, ipq4018_usb_hs_phynode_class,116ipq4018_usb_hs_phynode_methods,117sizeof(struct ipq4018_usb_hs_phynode_sc), phynode_usb_class);118119static int120ipq4018_usb_hs_usbphy_init_phy(struct ipq4018_usb_hs_phy_softc *sc,121phandle_t node)122{123struct phynode *phynode;124struct phynode_init_def phy_init;125struct ipq4018_usb_hs_phynode_sc *phy_sc;126int rv;127hwreset_t por_rst = NULL, srif_rst = NULL;128129/* FDT resources */130rv = hwreset_get_by_ofw_name(sc->dev, node, "por_rst", &por_rst);131if (rv != 0 && rv != ENOENT) {132device_printf(sc->dev, "Cannot get 'por_rst' reset\n");133goto fail;134}135rv = hwreset_get_by_ofw_name(sc->dev, node, "srif_rst", &srif_rst);136if (rv != 0 && rv != ENOENT) {137device_printf(sc->dev, "Cannot get 'srif_rst' reset\n");138goto fail;139}140141/* Create and register phy. */142bzero(&phy_init, sizeof(phy_init));143phy_init.id = 1;144phy_init.ofw_node = node;145phynode = phynode_create(sc->dev, &ipq4018_usb_hs_phynode_class,146&phy_init);147if (phynode == NULL) {148device_printf(sc->dev, "Cannot create phy.\n");149return (ENXIO);150}151152phy_sc = phynode_get_softc(phynode);153phy_sc->por_rst = por_rst;154phy_sc->srif_rst = srif_rst;155156if (phynode_register(phynode) == NULL) {157device_printf(sc->dev, "Cannot register phy.\n");158return (ENXIO);159}160161(void) ipq4018_usb_hs_phynode_phy_enable(phynode, true);162163return (0);164165fail:166if (por_rst != NULL)167hwreset_release(por_rst);168if (srif_rst != NULL)169hwreset_release(srif_rst);170171return (ENXIO);172}173174static int175ipq4018_usb_hs_usbphy_probe(device_t dev)176{177178if (!ofw_bus_status_okay(dev))179return (ENXIO);180181if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)182return (ENXIO);183184device_set_desc(dev, "IPQ4018/IPQ4019 USB HS PHY");185return (BUS_PROBE_DEFAULT);186}187188static int189ipq4018_usb_hs_usbphy_attach(device_t dev)190{191struct ipq4018_usb_hs_phy_softc *sc;192phandle_t node;193int rv;194195sc = device_get_softc(dev);196sc->dev = dev;197node = ofw_bus_get_node(sc->dev);198199rv = ipq4018_usb_hs_usbphy_init_phy(sc, node);200if (rv != 0)201goto fail;202bus_attach_children(dev);203return (0);204205fail:206return (ENXIO);207}208209static int210ipq4018_usb_hs_usbphy_detach(device_t dev)211{212213return (0);214}215216static device_method_t ipq4018_usb_hs_usbphy_methods[] = {217/* Device interface */218DEVMETHOD(device_probe, ipq4018_usb_hs_usbphy_probe),219DEVMETHOD(device_attach, ipq4018_usb_hs_usbphy_attach),220DEVMETHOD(device_detach, ipq4018_usb_hs_usbphy_detach),221DEVMETHOD_END222};223224static DEFINE_CLASS_0(ipq4018_usb_hs_usbphy, ipq4018_usb_hs_usbphy_driver,225ipq4018_usb_hs_usbphy_methods,226sizeof(struct ipq4018_usb_hs_phy_softc));227EARLY_DRIVER_MODULE(ipq4018_usb_hs_usbphy, simplebus,228ipq4018_usb_hs_usbphy_driver, NULL, NULL,229BUS_PASS_TIMER + BUS_PASS_ORDER_LAST);230231232