1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12#include <platsupport/io.h>
13#include <pci/pci.h>
14
15#include <usb/usb_host.h>
16#include "../../ehci/ehci.h"
17#include "../../services.h"
18
19#define USBLEGSUP            0x0
20#define USBLEGSUP_OS         BIT(24)
21#define USBLEGSUP_BIOS       BIT(16)
22#define USBLEGSUP_NEXT_SHF   BIT(8)
23#define USBLEGSUP_NEXT_MASK  0xFF
24#define USBLEGSUP_ID_SHF     BIT(0)
25#define USBLEGSUP_ID_MASK    0xFF
26
27#define USBLEGCTLSTS               0x4
28#define USBLEGCTLSTS_BAR           BIT(31)
29#define USBLEGCTLSTS_PCICMD        BIT(30)
30#define USBLEGCTLSTS_OSOC          BIT(29)
31#define USBLEGCTLSTS_AA            BIT(21)
32#define USBLEGCTLSTS_HSE           BIT(20)
33#define USBLEGCTLSTS_FLR           BIT(19)
34#define USBLEGCTLSTS_PCD           BIT(18)
35#define USBLEGCTLSTS_ERR           BIT(17)
36#define USBLEGCTLSTS_COMP          BIT(16)
37#define USBLEGCTLSTS_BAR_EN        BIT(15)
38#define USBLEGCTLSTS_PCICMD_EN     BIT(14)
39#define USBLEGCTLSTS_OSOC_EN       BIT(13)
40#define USBLEGCTLSTS_AA_EN         BIT(5)
41#define USBLEGCTLSTS_HSE_EN        BIT(4)
42#define USBLEGCTLSTS_FLR_EN        BIT(3)
43#define USBLEGCTLSTS_PC_EN         BIT(2)
44#define USBLEGCTLSTS_ERR_EN        BIT(1)
45#define USBLEGCTLSTS_SIM_EN        BIT(0)
46
47/* Host vendor ID and device ID */
48#define USB_HOST1_VID    0x8086
49#define USB_HOST1_DID    0x1E26
50#define USB_HOST2_VID    0x8086
51#define USB_HOST2_DID    0x1E2D
52
53/*
54 * TODO: Should get these numbers from IOAPIC tables. Remove them once we have a
55 * proper parser for the IOAPIC tables.
56 */
57#define USB_HOST1_IRQ    23
58#define USB_HOST2_IRQ    16
59
60static int _irq_line;
61
62static uintptr_t ehci_pci_init(uint16_t vid, uint16_t did,
63		ps_io_ops_t *io_ops)
64{
65	int err;
66	libpci_device_t *dev;
67	volatile struct ehci_host_cap *cap_regs;
68	uint32_t val;
69	uint8_t reg;
70
71	/* Find the device */
72	libpci_scan(io_ops->io_port_ops);
73	dev = libpci_find_device(vid, did);
74	if (dev) {
75		libpci_read_ioconfig(&dev->cfg, dev->bus, dev->dev, dev->fun);
76		/* Map device memory */
77		cap_regs = (volatile struct echi_host_cap*)MAP_DEVICE(io_ops,
78				dev->cfg.base_addr[0],
79				dev->cfg.base_addr_size[0]);
80		if (!cap_regs) {
81			ZF_LOGF("Invalid Registers\n");
82		}
83		_irq_line = dev->interrupt_line;
84	} else {
85		ZF_LOGF("EHCI: Host device not found!\n");
86	}
87
88	/* Check EHCI Extend Capabilities Pointer(Section 2.2.4) */
89	reg = EHCI_HCC_EECP(cap_regs->hccparams);
90	if (reg) {
91		reg += USBLEGSUP;
92		/* Take the ownership from BIOS */
93		val = libpci_read_reg32(dev->bus, dev->dev, dev->fun, reg);
94		val |= USBLEGSUP_OS;
95		libpci_write_reg32(dev->bus, dev->dev, dev->fun, reg, val);
96		do {
97			val = libpci_read_reg32(dev->bus, dev->dev,
98					dev->fun, reg);
99		} while (val & USBLEGSUP_BIOS);
100
101		if ((val >> USBLEGSUP_NEXT_SHF) & USBLEGSUP_NEXT_MASK) {
102			ZF_LOGW("EHCI: Warning! More Capability Registers.\n");
103		}
104	}
105
106	return (uintptr_t)cap_regs;
107}
108
109int
110usb_host_init(enum usb_host_id id, ps_io_ops_t* io_ops, ps_mutex_ops_t *sync,
111		usb_host_t* hdev)
112{
113	int err;
114	uint16_t vid, did;
115	uintptr_t usb_regs;
116
117	if (id < 0 || id > USB_NHOSTS) {
118		return -1;
119	}
120
121	if (!io_ops || !hdev) {
122		ZF_LOGF("Invalid arguments\n");
123	}
124
125	hdev->id = id;
126	hdev->dman = &io_ops->dma_manager;
127	hdev->sync = sync;
128
129	switch (id) {
130		case USB_HOST1:
131			vid = USB_HOST1_VID;
132			did = USB_HOST1_DID;
133			break;
134		case USB_HOST2:
135			vid = USB_HOST2_VID;
136			did = USB_HOST2_DID;
137			break;
138		default:
139			ZF_LOGF("Invalid host\n");
140			break;
141	}
142
143	/* Check device mappings */
144	usb_regs = ehci_pci_init(vid, did, io_ops);
145	if (!usb_regs) {
146		return -1;
147	}
148
149	err = ehci_host_init(hdev, usb_regs, NULL);
150
151	return err;
152}
153
154const int*
155usb_host_irqs(usb_host_t* host, int* nirqs)
156{
157	if (host->id < 0 || host->id > USB_NHOSTS) {
158		return NULL;
159	}
160
161	if (nirqs) {
162		*nirqs = 1;
163	}
164
165#ifdef CONFIG_IRQ_IOAPIC
166	switch (host->id) {
167		case USB_HOST1:
168			_irq_line = USB_HOST1_IRQ;
169			break;
170		case USB_HOST2:
171			_irq_line = USB_HOST2_IRQ;
172			break;
173		default:
174			ZF_LOGF("Invalid host\n");
175			break;
176	}
177#endif
178
179	host->irqs = &_irq_line;
180	return host->irqs;
181}
182
183