mirror of
git://git.ipxe.org/ipxe.git
synced 2025-10-14 06:41:46 +08:00
Compare commits
2 Commits
eca97c2ee2
...
e01e5ff7c6
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e01e5ff7c6 | ||
![]() |
6c42ea1275 |
131
src/drivers/usb/dwusb.c
Normal file
131
src/drivers/usb/dwusb.c
Normal 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
23
src/drivers/usb/dwusb.h
Normal 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 */
|
@@ -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 );
|
||||
|
||||
|
@@ -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 )
|
||||
|
@@ -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 */
|
Reference in New Issue
Block a user