| /* |
| * 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; |
| } |
| |