mirror of
https://github.com/apache/nuttx.git
synced 2026-10-03 20:27:53 +00:00
Nothing in the controller driver is PCI-specific beyond finding the registers and the interrupt, so an SoC wiring an xHCI controller directly could not use any of it. usbhost_xhci.c is now the driver; usbhost_xhci_pci.c is the PCI attachment, holding the ID table, the BAR mapping and the MSI-X vector. include/nuttx/usb/xhci.h carries what passes between them: the register base, a way to attach the interrupt, and a name to report the controller by, since a system may have more than one. The interrupt belongs to the bus: the bus attaches and detaches it, and the driver never holds an interrupt number. USBHOST_XHCI is the driver and is not selectable on its own; USBHOST_XHCI_PCI selects it. Another bus adds its own symbol beside it. No functional change intended. Assisted-by: Claude:claude-opus-5 Signed-off-by: Justin Hammond <justin@dynam.ac>
278 lines
7.5 KiB
C
278 lines
7.5 KiB
C
/****************************************************************************
|
|
* drivers/usbhost/usbhost_xhci_pci.c
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/* Finding an xHCI controller on a PCI bus.
|
|
*
|
|
* The controller itself is described by its specification and driven by
|
|
* usbhost_xhci.c, which is the same code wherever the part is fitted. This
|
|
* file is only the part that is true of PCI and of nothing else: which
|
|
* device identifiers to answer to, how to switch a device on and find its
|
|
* register window, and how its interrupt is arranged.
|
|
*/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
|
|
#include <nuttx/arch.h>
|
|
#include <nuttx/debug.h>
|
|
#include <nuttx/irq.h>
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/pci/pci.h>
|
|
#include <nuttx/usb/usbhost.h>
|
|
|
|
#include "usbhost_xhci.h"
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
/* What this file needs to remember about a controller, which is only the
|
|
* things the PCI layer will ask for again when the device goes away.
|
|
*/
|
|
|
|
struct pci_xhci_s
|
|
{
|
|
FAR struct pci_device_s *dev; /* The device we were given */
|
|
FAR struct usbhost_connection_s *conn; /* What the controller gave back */
|
|
int irq; /* Allocated MSI-X vector */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static int pci_xhci_irq_attach(FAR void *arg, xcpt_t handler,
|
|
FAR void *priv);
|
|
static void pci_xhci_irq_detach(FAR void *arg);
|
|
static int pci_xhci_probe(FAR struct pci_device_s *dev);
|
|
static void pci_xhci_remove(FAR struct pci_device_s *dev);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const struct xhci_bus_ops_s g_pci_xhci_ops =
|
|
{
|
|
.irq_attach = pci_xhci_irq_attach,
|
|
.irq_detach = pci_xhci_irq_detach,
|
|
};
|
|
|
|
/* PCI device table */
|
|
|
|
static const struct pci_device_id_s g_pci_xhci_id_table[] =
|
|
{
|
|
/* QEMU xHCI */
|
|
|
|
{
|
|
PCI_DEVICE(0x1b36, 0x000d),
|
|
.driver_data = 0
|
|
},
|
|
|
|
/* Intel Alder Lake USB 3.2 xHCI controller */
|
|
|
|
{
|
|
PCI_DEVICE(0x8086, 0x51ed),
|
|
.driver_data = 0
|
|
},
|
|
|
|
/* Intel Alder Lake-S USB 3.2 Gen 2x2 xHCI controller */
|
|
|
|
{
|
|
PCI_DEVICE(0x8086, 0x7ae0),
|
|
.driver_data = 0
|
|
},
|
|
{ }
|
|
};
|
|
|
|
/* PCI driver */
|
|
|
|
static struct pci_driver_s g_pci_xhci_drv =
|
|
{
|
|
.id_table = g_pci_xhci_id_table,
|
|
.probe = pci_xhci_probe,
|
|
.remove = pci_xhci_remove,
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: pci_xhci_irq_attach
|
|
*
|
|
* Description:
|
|
* Give the controller an interrupt. On PCI that means asking for a
|
|
* message rather than finding a wire, so the vector is allocated here and
|
|
* only then attached.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int pci_xhci_irq_attach(FAR void *arg, xcpt_t handler, FAR void *priv)
|
|
{
|
|
FAR struct pci_xhci_s *pcix = arg;
|
|
int ret;
|
|
|
|
ret = pci_alloc_irq(pcix->dev, &pcix->irq, 1);
|
|
if (ret != 1)
|
|
{
|
|
pcierr("Failed to allocate MSI %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
irq_attach(pcix->irq, handler, priv);
|
|
|
|
ret = pci_connect_irq(pcix->dev, &pcix->irq, 1);
|
|
if (ret != OK)
|
|
{
|
|
pcierr("Failed to connect MSI %d\n", ret);
|
|
pci_release_irq(pcix->dev, &pcix->irq, 1);
|
|
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
up_enable_irq(pcix->irq);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: pci_xhci_irq_detach
|
|
****************************************************************************/
|
|
|
|
static void pci_xhci_irq_detach(FAR void *arg)
|
|
{
|
|
FAR struct pci_xhci_s *pcix = arg;
|
|
|
|
up_disable_irq(pcix->irq);
|
|
irq_detach(pcix->irq);
|
|
pci_release_irq(pcix->dev, &pcix->irq, 1);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: pci_xhci_probe
|
|
*
|
|
* Description:
|
|
* Switch on a PCI xHCI controller and hand it to the controller driver.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int pci_xhci_probe(FAR struct pci_device_s *dev)
|
|
{
|
|
FAR struct pci_xhci_s *pcix;
|
|
uintptr_t base;
|
|
int ret;
|
|
|
|
pcix = kmm_zalloc(sizeof(struct pci_xhci_s));
|
|
if (pcix == NULL)
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
pcix->dev = dev;
|
|
dev->priv = pcix;
|
|
|
|
/* The controller has to be able to reach memory on its own, and its
|
|
* registers have to be answerable, before either is used.
|
|
*/
|
|
|
|
pci_set_master(dev);
|
|
pciinfo("Enabled bus mastering\n");
|
|
pci_enable_device(dev);
|
|
pciinfo("Enabled memory resources\n");
|
|
|
|
base = (uintptr_t)pci_map_bar(dev, 0);
|
|
if (base == 0)
|
|
{
|
|
pcierr("Not found BAR 0!\n");
|
|
ret = -EIO;
|
|
goto errout;
|
|
}
|
|
|
|
pcix->conn = xhci_initialize("usb", base, &g_pci_xhci_ops, pcix);
|
|
if (pcix->conn == NULL)
|
|
{
|
|
pcierr("xhci_initialize failed\n");
|
|
ret = -EIO;
|
|
goto errout;
|
|
}
|
|
|
|
return OK;
|
|
|
|
errout:
|
|
pci_clear_master(dev);
|
|
pci_disable_device(dev);
|
|
kmm_free(pcix);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: pci_xhci_remove
|
|
****************************************************************************/
|
|
|
|
static void pci_xhci_remove(FAR struct pci_device_s *dev)
|
|
{
|
|
FAR struct pci_xhci_s *pcix = dev->priv;
|
|
|
|
if (pcix == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
xhci_uninitialize(pcix->conn);
|
|
|
|
pci_clear_master(dev);
|
|
pci_disable_device(dev);
|
|
|
|
dev->priv = NULL;
|
|
kmm_free(pcix);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: pci_xhci_init
|
|
*
|
|
* Description:
|
|
* Initialize the USB host xHCI as PCI device.
|
|
*
|
|
* Input Parameters:
|
|
* None
|
|
*
|
|
* Returned Value:
|
|
* On success this function will return zero (OK); A negated errno value
|
|
* will be returned on failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int pci_xhci_init(void)
|
|
{
|
|
return pci_register_driver(&g_pci_xhci_drv);
|
|
}
|