blob: 4acb1f6fc63a49609ad00732e7aeaa9c17926e9e [file] [log] [blame] [edit]
/*
* Copyright 2017, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(DATA61_BSD)
*/
#include <platsupport/io.h>
#include <pci/pci.h>
#include <usb/usb_host.h>
#include "../../ehci/ehci.h"
#include "../../services.h"
#define USBLEGSUP 0x0
#define USBLEGSUP_OS BIT(24)
#define USBLEGSUP_BIOS BIT(16)
#define USBLEGSUP_NEXT_SHF BIT(8)
#define USBLEGSUP_NEXT_MASK 0xFF
#define USBLEGSUP_ID_SHF BIT(0)
#define USBLEGSUP_ID_MASK 0xFF
#define USBLEGCTLSTS 0x4
#define USBLEGCTLSTS_BAR BIT(31)
#define USBLEGCTLSTS_PCICMD BIT(30)
#define USBLEGCTLSTS_OSOC BIT(29)
#define USBLEGCTLSTS_AA BIT(21)
#define USBLEGCTLSTS_HSE BIT(20)
#define USBLEGCTLSTS_FLR BIT(19)
#define USBLEGCTLSTS_PCD BIT(18)
#define USBLEGCTLSTS_ERR BIT(17)
#define USBLEGCTLSTS_COMP BIT(16)
#define USBLEGCTLSTS_BAR_EN BIT(15)
#define USBLEGCTLSTS_PCICMD_EN BIT(14)
#define USBLEGCTLSTS_OSOC_EN BIT(13)
#define USBLEGCTLSTS_AA_EN BIT(5)
#define USBLEGCTLSTS_HSE_EN BIT(4)
#define USBLEGCTLSTS_FLR_EN BIT(3)
#define USBLEGCTLSTS_PC_EN BIT(2)
#define USBLEGCTLSTS_ERR_EN BIT(1)
#define USBLEGCTLSTS_SIM_EN BIT(0)
/* Host vendor ID and device ID */
#define USB_HOST1_VID 0x8086
#define USB_HOST1_DID 0x1E26
#define USB_HOST2_VID 0x8086
#define USB_HOST2_DID 0x1E2D
/*
* TODO: Should get these numbers from IOAPIC tables. Remove them once we have a
* proper parser for the IOAPIC tables.
*/
#define USB_HOST1_IRQ 23
#define USB_HOST2_IRQ 16
static int _irq_line;
static uintptr_t ehci_pci_init(uint16_t vid, uint16_t did,
ps_io_ops_t *io_ops)
{
int err;
libpci_device_t *dev;
volatile struct ehci_host_cap *cap_regs;
uint32_t val;
uint8_t reg;
/* Find the device */
libpci_scan(io_ops->io_port_ops);
dev = libpci_find_device(vid, did);
if (dev) {
libpci_read_ioconfig(&dev->cfg, dev->bus, dev->dev, dev->fun);
/* Map device memory */
cap_regs = (volatile struct echi_host_cap*)MAP_DEVICE(io_ops,
dev->cfg.base_addr[0],
dev->cfg.base_addr_size[0]);
if (!cap_regs) {
ZF_LOGF("Invalid Registers\n");
}
_irq_line = dev->interrupt_line;
} else {
ZF_LOGF("EHCI: Host device not found!\n");
}
/* Check EHCI Extend Capabilities Pointer(Section 2.2.4) */
reg = EHCI_HCC_EECP(cap_regs->hccparams);
if (reg) {
reg += USBLEGSUP;
/* Take the ownership from BIOS */
val = libpci_read_reg32(dev->bus, dev->dev, dev->fun, reg);
val |= USBLEGSUP_OS;
libpci_write_reg32(dev->bus, dev->dev, dev->fun, reg, val);
do {
val = libpci_read_reg32(dev->bus, dev->dev,
dev->fun, reg);
} while (val & USBLEGSUP_BIOS);
if ((val >> USBLEGSUP_NEXT_SHF) & USBLEGSUP_NEXT_MASK) {
ZF_LOGW("EHCI: Warning! More Capability Registers.\n");
}
}
return (uintptr_t)cap_regs;
}
int
usb_host_init(enum usb_host_id id, ps_io_ops_t* io_ops, ps_mutex_ops_t *sync,
usb_host_t* hdev)
{
int err;
uint16_t vid, did;
uintptr_t usb_regs;
if (id < 0 || id > USB_NHOSTS) {
return -1;
}
if (!io_ops || !hdev) {
ZF_LOGF("Invalid arguments\n");
}
hdev->id = id;
hdev->dman = &io_ops->dma_manager;
hdev->sync = sync;
switch (id) {
case USB_HOST1:
vid = USB_HOST1_VID;
did = USB_HOST1_DID;
break;
case USB_HOST2:
vid = USB_HOST2_VID;
did = USB_HOST2_DID;
break;
default:
ZF_LOGF("Invalid host\n");
break;
}
/* Check device mappings */
usb_regs = ehci_pci_init(vid, did, io_ops);
if (!usb_regs) {
return -1;
}
err = ehci_host_init(hdev, usb_regs, NULL);
return err;
}
const int*
usb_host_irqs(usb_host_t* host, int* nirqs)
{
if (host->id < 0 || host->id > USB_NHOSTS) {
return NULL;
}
if (nirqs) {
*nirqs = 1;
}
#ifdef CONFIG_IRQ_IOAPIC
switch (host->id) {
case USB_HOST1:
_irq_line = USB_HOST1_IRQ;
break;
case USB_HOST2:
_irq_line = USB_HOST2_IRQ;
break;
default:
ZF_LOGF("Invalid host\n");
break;
}
#endif
host->irqs = &_irq_line;
return host->irqs;
}