opnsense-src/sys/dev/usb/controller/dwc3.c
Bjoern A. Zeeb ec32fc2af5 dwc3: fix from not working
During the review of 09cdf4878c we
switched from cached registers to reading them as needed.
One read of the two reads was moved after the softreset got triggered
and as a result returned 0 rather than the proper register value.
Moving the read before the softreset gets initiated seems to make
things work again and xhci.c no longer complains about
"Controller does not support 4K page size.".

MFC after:	10 days
X-MFC with:	09cdf4878c
Pointy hat to:	bz
2022-07-05 00:53:42 +00:00

426 lines
12 KiB
C

/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2019 Emmanuel Vadot <manu@FreeBSD.Org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/condvar.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/gpio.h>
#include <machine/bus.h>
#include <dev/fdt/simplebus.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/ofw/ofw_subr.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usb_core.h>
#include <dev/usb/usb_busdma.h>
#include <dev/usb/usb_process.h>
#include <dev/usb/usb_controller.h>
#include <dev/usb/usb_bus.h>
#include <dev/usb/controller/xhci.h>
#include <dev/usb/controller/dwc3.h>
#include <dev/extres/clk/clk.h>
#include <dev/extres/phy/phy_usb.h>
#include "generic_xhci.h"
static struct ofw_compat_data compat_data[] = {
{ "snps,dwc3", 1 },
{ NULL, 0 }
};
struct snps_dwc3_softc {
struct xhci_softc sc;
device_t dev;
struct resource * mem_res;
bus_space_tag_t bst;
bus_space_handle_t bsh;
phandle_t node;
phy_t usb2_phy;
phy_t usb3_phy;
uint32_t snpsid;
};
#define DWC3_WRITE(_sc, _off, _val) \
bus_space_write_4(_sc->bst, _sc->bsh, _off, _val)
#define DWC3_READ(_sc, _off) \
bus_space_read_4(_sc->bst, _sc->bsh, _off)
static int
snps_dwc3_attach_xhci(device_t dev)
{
struct snps_dwc3_softc *snps_sc = device_get_softc(dev);
struct xhci_softc *sc = &snps_sc->sc;
int err = 0, rid = 0;
sc->sc_io_res = snps_sc->mem_res;
sc->sc_io_tag = snps_sc->bst;
sc->sc_io_hdl = snps_sc->bsh;
sc->sc_io_size = rman_get_size(snps_sc->mem_res);
sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
RF_SHAREABLE | RF_ACTIVE);
if (sc->sc_irq_res == NULL) {
device_printf(dev, "Failed to allocate IRQ\n");
return (ENXIO);
}
sc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
if (sc->sc_bus.bdev == NULL) {
device_printf(dev, "Failed to add USB device\n");
return (ENXIO);
}
device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
sprintf(sc->sc_vendor, "Synopsys");
device_set_desc(sc->sc_bus.bdev, "Synopsys");
err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
NULL, (driver_intr_t *)xhci_interrupt, sc, &sc->sc_intr_hdl);
if (err != 0) {
device_printf(dev, "Failed to setup IRQ, %d\n", err);
sc->sc_intr_hdl = NULL;
return (err);
}
err = xhci_init(sc, dev, 1);
if (err != 0) {
device_printf(dev, "Failed to init XHCI, with error %d\n", err);
return (ENXIO);
}
err = xhci_start_controller(sc);
if (err != 0) {
device_printf(dev, "Failed to start XHCI controller, with error %d\n", err);
return (ENXIO);
}
device_printf(sc->sc_bus.bdev, "trying to attach\n");
err = device_probe_and_attach(sc->sc_bus.bdev);
if (err != 0) {
device_printf(dev, "Failed to initialize USB, with error %d\n", err);
return (ENXIO);
}
return (0);
}
#ifdef DWC3_DEBUG
static void
snsp_dwc3_dump_regs(struct snps_dwc3_softc *sc, const char *msg)
{
struct xhci_softc *xsc;
uint32_t reg;
if (!bootverbose)
return;
device_printf(sc->dev, "%s: %s:\n", __func__, msg ? msg : "");
reg = DWC3_READ(sc, DWC3_GCTL);
device_printf(sc->dev, "GCTL: %#012x\n", reg);
reg = DWC3_READ(sc, DWC3_GUCTL);
device_printf(sc->dev, "GUCTL: %#012x\n", reg);
reg = DWC3_READ(sc, DWC3_GUCTL1);
device_printf(sc->dev, "GUCTL1: %#012x\n", reg);
reg = DWC3_READ(sc, DWC3_GUSB2PHYCFG0);
device_printf(sc->dev, "GUSB2PHYCFG0: %#012x\n", reg);
reg = DWC3_READ(sc, DWC3_GUSB3PIPECTL0);
device_printf(sc->dev, "GUSB3PIPECTL0: %#012x\n", reg);
reg = DWC3_READ(sc, DWC3_DCFG);
device_printf(sc->dev, "DCFG: %#012x\n", reg);
xsc = &sc->sc;
device_printf(sc->dev, "xhci quirks: %#012x\n", xsc->sc_quirks);
}
#endif
#ifdef DWC3_DEBUG
static void
snps_dwc3_dump_ctrlparams(struct snps_dwc3_softc *sc)
{
const bus_size_t offs[] = {
DWC3_GHWPARAMS0, DWC3_GHWPARAMS1, DWC3_GHWPARAMS2, DWC3_GHWPARAMS3,
DWC3_GHWPARAMS4, DWC3_GHWPARAMS5, DWC3_GHWPARAMS6, DWC3_GHWPARAMS7,
DWC3_GHWPARAMS8,
};
uint32_t reg;
int i;
for (i = 0; i < nitems(offs); i++) {
reg = DWC3_READ(sc, offs[i]);
if (bootverbose)
device_printf(sc->dev, "hwparams[%d]: %#012x\n", i, reg);
}
}
#endif
static void
snps_dwc3_reset(struct snps_dwc3_softc *sc)
{
uint32_t gctl, ghwp0, phy2, phy3;
if (sc->usb2_phy)
phy_enable(sc->usb2_phy);
if (sc->usb3_phy)
phy_enable(sc->usb3_phy);
ghwp0 = DWC3_READ(sc, DWC3_GHWPARAMS0);
gctl = DWC3_READ(sc, DWC3_GCTL);
gctl |= DWC3_GCTL_CORESOFTRESET;
DWC3_WRITE(sc, DWC3_GCTL, gctl);
phy2 = DWC3_READ(sc, DWC3_GUSB2PHYCFG0);
phy2 |= DWC3_GUSB2PHYCFG0_PHYSOFTRST;
if ((ghwp0 & DWC3_GHWPARAMS0_MODE_MASK) ==
DWC3_GHWPARAMS0_MODE_DUALROLEDEVICE)
phy2 &= ~DWC3_GUSB2PHYCFG0_SUSPENDUSB20;
DWC3_WRITE(sc, DWC3_GUSB2PHYCFG0, phy2);
phy3 = DWC3_READ(sc, DWC3_GUSB3PIPECTL0);
phy3 |= DWC3_GUSB3PIPECTL0_PHYSOFTRST;
if ((ghwp0 & DWC3_GHWPARAMS0_MODE_MASK) ==
DWC3_GHWPARAMS0_MODE_DUALROLEDEVICE)
phy3 &= ~DWC3_GUSB3PIPECTL0_SUSPENDUSB3;
DWC3_WRITE(sc, DWC3_GUSB3PIPECTL0, phy3);
DELAY(1000);
phy2 &= ~DWC3_GUSB2PHYCFG0_PHYSOFTRST;
DWC3_WRITE(sc, DWC3_GUSB2PHYCFG0, phy2);
phy3 &= ~DWC3_GUSB3PIPECTL0_PHYSOFTRST;
DWC3_WRITE(sc, DWC3_GUSB3PIPECTL0, phy3);
gctl &= ~DWC3_GCTL_CORESOFTRESET;
DWC3_WRITE(sc, DWC3_GCTL, gctl);
}
static void
snps_dwc3_configure_host(struct snps_dwc3_softc *sc)
{
uint32_t reg;
reg = DWC3_READ(sc, DWC3_GCTL);
reg &= ~DWC3_GCTL_PRTCAPDIR_MASK;
reg |= DWC3_GCTL_PRTCAPDIR_HOST;
DWC3_WRITE(sc, DWC3_GCTL, reg);
/*
* Enable the Host IN Auto Retry feature, making the
* host respond with a non-terminating retry ACK.
* XXX If we ever support more than host mode this needs a dr_mode check.
*/
reg = DWC3_READ(sc, DWC3_GUCTL);
reg |= DWC3_GUCTL_HOST_AUTO_RETRY;
DWC3_WRITE(sc, DWC3_GUCTL, reg);
}
static void
snps_dwc3_configure_phy(struct snps_dwc3_softc *sc)
{
char *phy_type;
uint32_t reg;
int nphy_types;
phy_type = NULL;
nphy_types = OF_getprop_alloc(sc->node, "phy_type", (void **)&phy_type);
if (nphy_types <= 0)
return;
reg = DWC3_READ(sc, DWC3_GUSB2PHYCFG0);
if (strncmp(phy_type, "utmi_wide", 9) == 0) {
reg &= ~(DWC3_GUSB2PHYCFG0_PHYIF | DWC3_GUSB2PHYCFG0_USBTRDTIM(0xf));
reg |= DWC3_GUSB2PHYCFG0_PHYIF |
DWC3_GUSB2PHYCFG0_USBTRDTIM(DWC3_GUSB2PHYCFG0_USBTRDTIM_16BITS);
} else {
reg &= ~(DWC3_GUSB2PHYCFG0_PHYIF | DWC3_GUSB2PHYCFG0_USBTRDTIM(0xf));
reg |= DWC3_GUSB2PHYCFG0_PHYIF |
DWC3_GUSB2PHYCFG0_USBTRDTIM(DWC3_GUSB2PHYCFG0_USBTRDTIM_8BITS);
}
DWC3_WRITE(sc, DWC3_GUSB2PHYCFG0, reg);
OF_prop_free(phy_type);
}
static void
snps_dwc3_do_quirks(struct snps_dwc3_softc *sc)
{
struct xhci_softc *xsc;
uint32_t ghwp0, reg;
ghwp0 = DWC3_READ(sc, DWC3_GHWPARAMS0);
reg = DWC3_READ(sc, DWC3_GUSB2PHYCFG0);
if (device_has_property(sc->dev, "snps,dis-u2-freeclk-exists-quirk"))
reg &= ~DWC3_GUSB2PHYCFG0_U2_FREECLK_EXISTS;
else
reg |= DWC3_GUSB2PHYCFG0_U2_FREECLK_EXISTS;
if (device_has_property(sc->dev, "snps,dis_u2_susphy_quirk"))
reg &= ~DWC3_GUSB2PHYCFG0_SUSPENDUSB20;
else if ((ghwp0 & DWC3_GHWPARAMS0_MODE_MASK) ==
DWC3_GHWPARAMS0_MODE_DUALROLEDEVICE)
reg |= DWC3_GUSB2PHYCFG0_SUSPENDUSB20;
if (device_has_property(sc->dev, "snps,dis_enblslpm_quirk"))
reg &= ~DWC3_GUSB2PHYCFG0_ENBLSLPM;
else
reg |= DWC3_GUSB2PHYCFG0_ENBLSLPM;
DWC3_WRITE(sc, DWC3_GUSB2PHYCFG0, reg);
reg = DWC3_READ(sc, DWC3_GUCTL1);
if (device_has_property(sc->dev, "snps,dis-tx-ipgap-linecheck-quirk"))
reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS;
DWC3_WRITE(sc, DWC3_GUCTL1, reg);
reg = DWC3_READ(sc, DWC3_GUSB3PIPECTL0);
if (device_has_property(sc->dev, "snps,dis-del-phy-power-chg-quirk"))
reg &= ~DWC3_GUSB3PIPECTL0_DELAYP1TRANS;
if (device_has_property(sc->dev, "snps,dis_rxdet_inp3_quirk"))
reg |= DWC3_GUSB3PIPECTL0_DISRXDETINP3;
if (device_has_property(sc->dev, "snps,dis_u3_susphy_quirk"))
reg &= ~DWC3_GUSB3PIPECTL0_SUSPENDUSB3;
else if ((ghwp0 & DWC3_GHWPARAMS0_MODE_MASK) ==
DWC3_GHWPARAMS0_MODE_DUALROLEDEVICE)
reg |= DWC3_GUSB3PIPECTL0_SUSPENDUSB3;
DWC3_WRITE(sc, DWC3_GUSB3PIPECTL0, reg);
/* Port Disable does not work on <= 3.00a. Disable PORT_PED. */
if ((sc->snpsid & 0xffff) <= 0x300a) {
xsc = &sc->sc;
xsc->sc_quirks |= XHCI_QUIRK_DISABLE_PORT_PED;
}
}
static int
snps_dwc3_probe(device_t dev)
{
char dr_mode[16];
ssize_t s;
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
return (ENXIO);
s = device_get_property(dev, "dr_mode", dr_mode, sizeof(dr_mode),
DEVICE_PROP_BUFFER);
if (s == -1) {
device_printf(dev, "Cannot determine dr_mode\n");
return (ENXIO);
}
if (strcmp(dr_mode, "host") != 0) {
device_printf(dev,
"Found dr_mode '%s' but only 'host' supported. s=%zd\n",
dr_mode, s);
return (ENXIO);
}
device_set_desc(dev, "Synopsys Designware DWC3");
return (BUS_PROBE_DEFAULT);
}
static int
snps_dwc3_attach(device_t dev)
{
struct snps_dwc3_softc *sc;
int rid = 0;
sc = device_get_softc(dev);
sc->dev = dev;
sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
RF_ACTIVE);
if (sc->mem_res == NULL) {
device_printf(dev, "Failed to map memory\n");
return (ENXIO);
}
sc->bst = rman_get_bustag(sc->mem_res);
sc->bsh = rman_get_bushandle(sc->mem_res);
sc->snpsid = DWC3_READ(sc, DWC3_GSNPSID);
if (bootverbose)
device_printf(sc->dev, "snps id: %#012x\n", sc->snpsid);
#ifdef DWC3_DEBUG
snps_dwc3_dump_ctrlparams(sc);
#endif
/* Get the phys */
sc->node = ofw_bus_get_node(dev);
phy_get_by_ofw_name(dev, sc->node, "usb2-phy", &sc->usb2_phy);
phy_get_by_ofw_name(dev, sc->node, "usb3-phy", &sc->usb3_phy);
snps_dwc3_reset(sc);
snps_dwc3_configure_host(sc);
snps_dwc3_configure_phy(sc);
snps_dwc3_do_quirks(sc);
#ifdef DWC3_DEBUG
snsp_dwc3_dump_regs(sc, "Pre XHCI init");
#endif
snps_dwc3_attach_xhci(dev);
#ifdef DWC3_DEBUG
snsp_dwc3_dump_regs(sc, "Post XHCI init");
#endif
return (0);
}
static device_method_t snps_dwc3_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, snps_dwc3_probe),
DEVMETHOD(device_attach, snps_dwc3_attach),
DEVMETHOD_END
};
static driver_t snps_dwc3_driver = {
"xhci",
snps_dwc3_methods,
sizeof(struct snps_dwc3_softc)
};
DRIVER_MODULE(snps_dwc3, simplebus, snps_dwc3_driver, 0, 0);
MODULE_DEPEND(snps_dwc3, xhci, 1, 1, 1);