1
0
mirror of git://git.ipxe.org/ipxe.git synced 2025-10-14 06:41:46 +08:00

Compare commits

...

2 Commits

Author SHA1 Message Date
Michael Brown
e01e5ff7c6 [dwusb] Add driver for DesignWare USB3 host controller
Add a basic driver for the DesignWare USB3 host controller as found in
the Lichee Pi 4A.

This driver covers only the DesignWare host controller hardware.  On
the Lichee Pi 4A, this is sufficient to get the single USB root hub
port (exposed internally via the SODIMM connector) up and running.

The driver does not yet handle the various GPIOs that control power
and signal routing for the Lichee Pi 4A's onboard VL817 USB hub and
the four physical USB-A ports.  This therefore leaves the USB hub and
the USB-A ports unpowered, and the USB2 root hub port routed to the
physical USB-C port.  Devices plugged in to the USB-A ports will not
be powered up, and a device plugged in to the USB-C port will
enumerate as a USB2 device.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2025-07-21 15:55:13 +01:00
Michael Brown
6c42ea1275 [xhci] Allow for non-PCI xHCI host controllers
Allow for the existence of xHCI host controllers where the underlying
hardware is not a PCI device.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2025-07-21 15:33:58 +01:00
5 changed files with 250 additions and 47 deletions

131
src/drivers/usb/dwusb.c Normal file
View File

@@ -0,0 +1,131 @@
/*
* Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ipxe/timer.h>
#include <ipxe/devtree.h>
#include <ipxe/fdt.h>
#include "dwusb.h"
/** @file
*
* Synopsys DesignWare USB3 host controller driver
*
*/
/**
* Probe devicetree device
*
* @v dt Devicetree device
* @v offset Starting node offset
* @ret rc Return status code
*/
static int dwusb_probe ( struct dt_device *dt, unsigned int offset ) {
struct xhci_device *xhci;
uint32_t gctl;
int rc;
/* Allocate and initialise structure */
xhci = zalloc ( sizeof ( *xhci ) );
if ( ! xhci ) {
rc = -ENOMEM;
goto err_alloc;
}
xhci->dev = &dt->dev;
xhci->dma = &dt->dma;
/* Map registers */
xhci->regs = dt_ioremap ( dt, offset, 0, 0 );
if ( ! xhci->regs ) {
rc = -ENODEV;
goto err_ioremap;
}
/* Reset via global core control register */
gctl = readl ( xhci->regs + DWUSB_GCTL );
writel ( ( gctl | DWUSB_GCTL_RESET ), ( xhci->regs + DWUSB_GCTL ) );
mdelay ( 100 );
writel ( gctl, ( xhci->regs + DWUSB_GCTL ) );
/* Configure as a host controller */
gctl &= ~DWUSB_GCTL_PRTDIR_MASK;
gctl |= DWUSB_GCTL_PRTDIR_HOST;
writel ( gctl, ( xhci->regs + DWUSB_GCTL ) );
/* Initialise xHCI device */
xhci_init ( xhci );
/* Register xHCI device */
if ( ( rc = xhci_register ( xhci ) ) != 0 ) {
DBGC ( xhci, "XHCI %s could not register: %s\n",
xhci->name, strerror ( rc ) );
goto err_register;
}
dt_set_drvdata ( dt, xhci );
return 0;
xhci_unregister ( xhci );
err_register:
iounmap ( xhci->regs );
err_ioremap:
free ( xhci );
err_alloc:
return rc;
}
/**
* Remove devicetree device
*
* @v dt Devicetree device
*/
static void dwusb_remove ( struct dt_device *dt ) {
struct xhci_device *xhci = dt_get_drvdata ( dt );
/* Unregister xHCI device */
xhci_unregister ( xhci );
/* Unmap registers */
iounmap ( xhci->regs );
/* Free device */
free ( xhci );
}
/** DesignWare USB3 compatible model identifiers */
static const char * dwusb_ids[] = {
"snps,dwc3",
};
/** DesignWare USB3 devicetree driver */
struct dt_driver dwusb_driver __dt_driver = {
.name = "dwusb",
.ids = dwusb_ids,
.id_count = ( sizeof ( dwusb_ids ) / sizeof ( dwusb_ids[0] ) ),
.probe = dwusb_probe,
.remove = dwusb_remove,
};

23
src/drivers/usb/dwusb.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef _DWUSB_H
#define _DWUSB_H
/** @file
*
* Synopsys DesignWare USB3 host controller driver
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/xhci.h>
/** Global core control register */
#define DWUSB_GCTL 0xc110
#define DWUSB_GCTL_PRTDIR( x ) ( (x) << 12 ) /**< Port direction */
#define DWUSB_GCTL_PRTDIR_HOST \
DWUSB_GCTL_PRTDIR ( 1 ) /**< Operate as a host */
#define DWUSB_GCTL_PRTDIR_MASK \
DWUSB_GCTL_PRTDIR ( 3 ) /**< Port direction mask */
#define DWUSB_GCTL_RESET 0x00000800 /**< Core soft reset */
#endif /* _DWUSB_H */

View File

@@ -35,7 +35,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/usb.h>
#include <ipxe/init.h>
#include <ipxe/profile.h>
#include "xhci.h"
#include <ipxe/xhci.h>
/** @file
*
@@ -259,9 +259,8 @@ static struct profiler xhci_transfer_profiler __profiler =
* Initialise device
*
* @v xhci xHCI device
* @v regs MMIO registers
*/
static void xhci_init ( struct xhci_device *xhci, void *regs ) {
void xhci_init ( struct xhci_device *xhci ) {
uint32_t hcsparams1;
uint32_t hcsparams2;
uint32_t hccparams1;
@@ -270,8 +269,11 @@ static void xhci_init ( struct xhci_device *xhci, void *regs ) {
size_t rtsoff;
size_t dboff;
/* Set device name */
xhci->name = xhci->dev->name;
/* Locate capability, operational, runtime, and doorbell registers */
xhci->cap = regs;
xhci->cap = xhci->regs;
caplength = readb ( xhci->cap + XHCI_CAP_CAPLENGTH );
rtsoff = readl ( xhci->cap + XHCI_CAP_RTSOFF );
dboff = readl ( xhci->cap + XHCI_CAP_DBOFF );
@@ -310,6 +312,10 @@ static void xhci_init ( struct xhci_device *xhci, void *regs ) {
assert ( ( ( xhci->pagesize ) & ( xhci->pagesize - 1 ) ) == 0 );
DBGC2 ( xhci, "XHCI %s page size %zd bytes\n",
xhci->name, xhci->pagesize );
/* Configure DMA device */
if ( xhci->dma && xhci->addr64 )
dma_set_mask_64bit ( xhci->dma );
}
/**
@@ -3264,7 +3270,7 @@ static int xhci_root_clear_tt ( struct usb_hub *hub, struct usb_port *port,
/******************************************************************************
*
* PCI interface
* Hardware-independent interface
*
******************************************************************************
*/
@@ -3303,6 +3309,75 @@ static struct usb_host_operations xhci_operations = {
},
};
/**
* Register xHCI controller
*
* @v xhci xHCI device
* @ret rc Return status code
*/
int xhci_register ( struct xhci_device *xhci ) {
struct usb_port *port;
unsigned int i;
int rc;
/* Reset device */
if ( ( rc = xhci_reset ( xhci ) ) != 0 )
goto err_reset;
/* Allocate USB bus */
xhci->bus = alloc_usb_bus ( xhci->dev, xhci->ports, XHCI_MTU,
&xhci_operations );
if ( ! xhci->bus ) {
rc = -ENOMEM;
goto err_alloc_bus;
}
usb_bus_set_hostdata ( xhci->bus, xhci );
usb_hub_set_drvdata ( xhci->bus->hub, xhci );
/* Set port protocols */
for ( i = 1 ; i <= xhci->ports ; i++ ) {
port = usb_port ( xhci->bus->hub, i );
port->protocol = xhci_port_protocol ( xhci, i );
}
/* Register USB bus */
if ( ( rc = register_usb_bus ( xhci->bus ) ) != 0 )
goto err_register;
return 0;
unregister_usb_bus ( xhci->bus );
err_register:
free_usb_bus ( xhci->bus );
err_alloc_bus:
xhci_reset ( xhci );
err_reset:
return rc;
}
/**
* Unregister xHCI controller
*
* @v xhci xHCI device
*/
void xhci_unregister ( struct xhci_device *xhci ) {
struct usb_bus *bus = xhci->bus;
/* Unregister and free USB bus */
unregister_usb_bus ( bus );
free_usb_bus ( bus );
/* Reset device */
xhci_reset ( xhci );
}
/******************************************************************************
*
* PCI interface
*
******************************************************************************
*/
/**
* Fix Intel PCH-specific quirks
*
@@ -3365,10 +3440,8 @@ static void xhci_pch_undo ( struct xhci_device *xhci, struct pci_device *pci ) {
*/
static int xhci_probe ( struct pci_device *pci ) {
struct xhci_device *xhci;
struct usb_port *port;
unsigned long bar_start;
size_t bar_size;
unsigned int i;
int rc;
/* Allocate and initialise structure */
@@ -3377,7 +3450,8 @@ static int xhci_probe ( struct pci_device *pci ) {
rc = -ENOMEM;
goto err_alloc;
}
xhci->name = pci->dev.name;
xhci->dev = &pci->dev;
xhci->dma = &pci->dma;
xhci->quirks = pci->id->driver_data;
/* Fix up PCI device */
@@ -3393,12 +3467,7 @@ static int xhci_probe ( struct pci_device *pci ) {
}
/* Initialise xHCI device */
xhci_init ( xhci, xhci->regs );
/* Configure DMA device */
xhci->dma = &pci->dma;
if ( xhci->addr64 )
dma_set_mask_64bit ( xhci->dma );
xhci_init ( xhci );
/* Initialise USB legacy support and claim ownership */
xhci_legacy_init ( xhci );
@@ -3408,39 +3477,15 @@ static int xhci_probe ( struct pci_device *pci ) {
if ( xhci->quirks & XHCI_PCH )
xhci_pch_fix ( xhci, pci );
/* Reset device */
if ( ( rc = xhci_reset ( xhci ) ) != 0 )
goto err_reset;
/* Allocate USB bus */
xhci->bus = alloc_usb_bus ( &pci->dev, xhci->ports, XHCI_MTU,
&xhci_operations );
if ( ! xhci->bus ) {
rc = -ENOMEM;
goto err_alloc_bus;
}
usb_bus_set_hostdata ( xhci->bus, xhci );
usb_hub_set_drvdata ( xhci->bus->hub, xhci );
/* Set port protocols */
for ( i = 1 ; i <= xhci->ports ; i++ ) {
port = usb_port ( xhci->bus->hub, i );
port->protocol = xhci_port_protocol ( xhci, i );
}
/* Register USB bus */
if ( ( rc = register_usb_bus ( xhci->bus ) ) != 0 )
/* Register xHCI device */
if ( ( rc = xhci_register ( xhci ) ) != 0 )
goto err_register;
pci_set_drvdata ( pci, xhci );
return 0;
unregister_usb_bus ( xhci->bus );
xhci_unregister ( xhci );
err_register:
free_usb_bus ( xhci->bus );
err_alloc_bus:
xhci_reset ( xhci );
err_reset:
if ( xhci->quirks & XHCI_PCH )
xhci_pch_undo ( xhci, pci );
xhci_legacy_release ( xhci );
@@ -3458,7 +3503,6 @@ static int xhci_probe ( struct pci_device *pci ) {
*/
static void xhci_remove ( struct pci_device *pci ) {
struct xhci_device *xhci = pci_get_drvdata ( pci );
struct usb_bus *bus = xhci->bus;
uint16_t command;
/* Some systems are observed to disable bus mastering on
@@ -3473,12 +3517,10 @@ static void xhci_remove ( struct pci_device *pci ) {
xhci_fail ( xhci );
}
/* Unregister and free USB bus */
unregister_usb_bus ( bus );
free_usb_bus ( bus );
/* Unregister xHCI controller */
xhci_unregister ( xhci );
/* Reset device and undo any PCH-specific fixes */
xhci_reset ( xhci );
/* Undo any PCH-specific fixes */
if ( xhci->quirks & XHCI_PCH )
xhci_pch_undo ( xhci, pci );

View File

@@ -237,6 +237,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_devtree ( ERRFILE_DRIVER | 0x00da0000 )
#define ERRFILE_cgem ( ERRFILE_DRIVER | 0x00db0000 )
#define ERRFILE_dwmac ( ERRFILE_DRIVER | 0x00dc0000 )
#define ERRFILE_dwusb ( ERRFILE_DRIVER | 0x00dd0000 )
#define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 )
#define ERRFILE_arp ( ERRFILE_NET | 0x00010000 )

View File

@@ -1066,6 +1066,8 @@ struct xhci_scratchpad {
struct xhci_device {
/** Registers */
void *regs;
/** Underlying hardware device */
struct device *dev;
/** DMA device */
struct dma_device *dma;
/** Name */
@@ -1175,4 +1177,8 @@ struct xhci_endpoint {
struct xhci_trb_ring ring;
};
extern void xhci_init ( struct xhci_device *xhci );
extern int xhci_register ( struct xhci_device *xhci );
extern void xhci_unregister ( struct xhci_device *xhci );
#endif /* _IPXE_XHCI_H */