|
@@ -37,6 +37,8 @@ void xhci_hc_irq_handler(uint64_t irq_num, uint64_t cid, struct pt_regs *regs);
|
|
|
static int xhci_hc_init_intr(int id);
|
|
|
static int xhci_hc_start_ports(int id);
|
|
|
|
|
|
+static int xhci_send_command(int id, struct xhci_TRB_t *trb, const bool do_ring);
|
|
|
+
|
|
|
hardware_intr_controller xhci_hc_intr_controller =
|
|
|
{
|
|
|
.enable = xhci_hc_irq_enable,
|
|
@@ -55,9 +57,6 @@ hardware_intr_controller xhci_hc_intr_controller =
|
|
|
例子:不能在一个32bit的寄存器中的偏移量8的位置开始读取1个字节
|
|
|
这种情况下,我们必须从32bit的寄存器的0地址处开始读取32bit,然后通过移位的方式得到其中的字节。
|
|
|
*/
|
|
|
-#define xhci_read_cap_reg8(id, offset) (*(uint8_t *)(xhci_hc[id].vbase + offset))
|
|
|
-#define xhci_get_ptr_cap_reg8(id, offset) ((uint8_t *)(xhci_hc[id].vbase + offset))
|
|
|
-#define xhci_write_cap_reg8(id, offset, value) (*(uint8_t *)(xhci_hc[id].vbase + offset) = (uint8_t)value)
|
|
|
|
|
|
#define xhci_read_cap_reg32(id, offset) (*(uint32_t *)(xhci_hc[id].vbase + offset))
|
|
|
#define xhci_get_ptr_cap_reg32(id, offset) ((uint32_t *)(xhci_hc[id].vbase + offset))
|
|
@@ -79,6 +78,11 @@ hardware_intr_controller xhci_hc_intr_controller =
|
|
|
#define xhci_get_ptr_op_reg64(id, offset) ((uint64_t *)(xhci_hc[id].vbase_op + offset))
|
|
|
#define xhci_write_op_reg64(id, offset, value) (*(uint64_t *)(xhci_hc[id].vbase_op + offset) = (uint64_t)value)
|
|
|
|
|
|
+#define xhci_write_mem32(vaddr, value) (*(uint32_t *)(vaddr) = value)
|
|
|
+#define xhci_write_mem64(vaddr, value) (*(uint64_t *)(vaddr) = value)
|
|
|
+#define xhci_read_mem32(vaddr) (*(uint32_t *)(vaddr))
|
|
|
+#define xhci_read_mem64(vaddr) (*(uint64_t *)(vaddr))
|
|
|
+
|
|
|
/**
|
|
|
* @brief 计算中断寄存器组虚拟地址
|
|
|
* @param id 主机控制器id
|
|
@@ -267,7 +271,7 @@ static int xhci_hc_stop_legacy(int id)
|
|
|
do
|
|
|
{
|
|
|
// 判断当前entry是否为legacy support entry
|
|
|
- if (xhci_read_cap_reg8(id, current_offset) == XHCI_XECP_ID_LEGACY)
|
|
|
+ if ((xhci_read_cap_reg32(id, current_offset) & 0xff) == XHCI_XECP_ID_LEGACY)
|
|
|
{
|
|
|
io_mfence();
|
|
|
// 接管控制权
|
|
@@ -309,7 +313,7 @@ static int xhci_hc_stop_legacy(int id)
|
|
|
static int xhci_hc_start_sched(int id)
|
|
|
{
|
|
|
io_mfence();
|
|
|
- xhci_write_op_reg32(id, XHCI_OPS_USBCMD, (1 << 0) | (1 >> 2) | (1 << 3));
|
|
|
+ xhci_write_op_reg32(id, XHCI_OPS_USBCMD, (1 << 0) | (1 << 2) | (1 << 3));
|
|
|
io_mfence();
|
|
|
usleep(100 * 1000);
|
|
|
}
|
|
@@ -327,12 +331,6 @@ static int xhci_hc_stop_sched(int id)
|
|
|
io_mfence();
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * @brief
|
|
|
- *
|
|
|
- * @return uint32_t
|
|
|
- */
|
|
|
-
|
|
|
/**
|
|
|
* @brief 在Ex capability list中寻找符合指定的协议号的寄存器offset、count、flag信息
|
|
|
*
|
|
@@ -588,17 +586,20 @@ uint64_t xhci_hc_irq_install(uint64_t irq_num, void *arg)
|
|
|
struct msi_desc_t msi_desc;
|
|
|
memset(&msi_desc, 0, sizeof(struct msi_desc_t));
|
|
|
io_mfence();
|
|
|
+ msi_desc.irq_num = irq_num;
|
|
|
+ msi_desc.msi_index = 0;
|
|
|
msi_desc.pci_dev = (struct pci_device_structure_header_t *)xhci_hc[cid].pci_dev_hdr;
|
|
|
msi_desc.assert = info->assert;
|
|
|
msi_desc.edge_trigger = info->edge_trigger;
|
|
|
msi_desc.processor = info->processor;
|
|
|
msi_desc.pci.msi_attribute.is_64 = 1;
|
|
|
- msi_desc.pci.msi_attribute.is_msix=1;
|
|
|
+ msi_desc.pci.msi_attribute.is_msix = 1;
|
|
|
// todo: QEMU是使用msix的,因此要先在pci中实现msix
|
|
|
io_mfence();
|
|
|
int retval = pci_enable_msi(&msi_desc);
|
|
|
kdebug("pci retval = %d", retval);
|
|
|
kdebug("xhci irq %d installed.", irq_num);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -764,6 +765,7 @@ static int xhci_hc_start_ports(int id)
|
|
|
}
|
|
|
}
|
|
|
kinfo("xHCI controller %d: Started %d ports.", id, cnt);
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -833,6 +835,79 @@ static int xhci_hc_init_intr(int id)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * @brief 写入doorbell寄存器
|
|
|
+ *
|
|
|
+ * @param id 主机控制器id
|
|
|
+ * @param slot_id usb控制器插槽id(0用作命令门铃,其他的用于具体的设备的门铃)
|
|
|
+ * @param value 要写入的值
|
|
|
+ */
|
|
|
+static __always_inline void __xhci_write_doorbell(const int id, const uint16_t slot_id, const uint32_t value)
|
|
|
+{
|
|
|
+ // 确保写入门铃寄存器之前,所有的写操作均已完成
|
|
|
+ io_sfence();
|
|
|
+ xhci_write_cap_reg32(id, xhci_hc[id].db_offset + slot_id * sizeof(uint32_t), value);
|
|
|
+ io_sfence();
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief 往xhci控制器发送命令
|
|
|
+ *
|
|
|
+ * @param id xhci控制器号
|
|
|
+ * @param trb 传输请求块
|
|
|
+ * @param do_ring 是否通知doorbell register
|
|
|
+ * @return int 错误码
|
|
|
+ */
|
|
|
+static int xhci_send_command(int id, struct xhci_TRB_t *trb, const bool do_ring)
|
|
|
+{
|
|
|
+ uint64_t origin_trb_vaddr = xhci_hc[id].cmd_trb_vaddr;
|
|
|
+
|
|
|
+ // 必须先写入参数和状态数据,最后写入command
|
|
|
+ xhci_write_mem64(xhci_hc[id].cmd_trb_vaddr, trb->param); // 参数
|
|
|
+ xhci_write_mem32(xhci_hc[id].cmd_trb_vaddr + 8, trb->status); // 状态
|
|
|
+ xhci_write_mem32(xhci_hc[id].cmd_trb_vaddr + 12, trb->command); // 命令
|
|
|
+
|
|
|
+ xhci_hc[id].cmd_trb_vaddr += sizeof(struct xhci_TRB_t); // 跳转到下一个trb
|
|
|
+
|
|
|
+ {
|
|
|
+ // 如果下一个trb是link trb,则将下一个要操作的地址是设置为第一个trb
|
|
|
+ struct xhci_TRB_normal_t *ptr = (struct xhci_TRB_normal_t *)xhci_hc[id].cmd_trb_vaddr;
|
|
|
+ if (ptr->TRB_type == TRB_TYPE_LINK)
|
|
|
+ {
|
|
|
+ ptr->cycle = xhci_hc[id].cmd_trb_cycle;
|
|
|
+ xhci_hc[id].cmd_trb_vaddr = xhci_hc[id].cmd_ring_vaddr;
|
|
|
+ xhci_hc[id].cmd_trb_cycle ^= 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (do_ring) // 按响命令门铃
|
|
|
+ {
|
|
|
+ kdebug("to ring..");
|
|
|
+ __xhci_write_doorbell(id, 0, 0);
|
|
|
+ kdebug("ring..");
|
|
|
+ // 等待中断产生
|
|
|
+ int timer = 20;
|
|
|
+
|
|
|
+ // Now wait for the interrupt to happen
|
|
|
+ // We use bit 31 of the command dword since it is reserved
|
|
|
+
|
|
|
+ while (timer && ((xhci_read_mem32(origin_trb_vaddr + 12) & (1 << 31)) == 0))
|
|
|
+ {
|
|
|
+ usleep(1000);
|
|
|
+ --timer;
|
|
|
+ }
|
|
|
+ uint32_t x = xhci_read_cap_reg32(id, xhci_hc[id].rts_offset + 0x20);
|
|
|
+ kdebug("ip=%#010lx", x);
|
|
|
+ if (timer == 0)
|
|
|
+ kwarn("USB xHCI Command Interrupt wait timed out.");
|
|
|
+ else
|
|
|
+ {
|
|
|
+ kdebug("interrupt done");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* @brief 初始化xhci控制器
|
|
|
*
|
|
@@ -861,7 +936,12 @@ void xhci_init(struct pci_device_structure_general_device_t *dev_hdr)
|
|
|
xhci_hc[cid].controller_id = cid;
|
|
|
xhci_hc[cid].pci_dev_hdr = dev_hdr;
|
|
|
io_mfence();
|
|
|
- pci_write_config(dev_hdr->header.bus, dev_hdr->header.device, dev_hdr->header.func, 0x4, 0x0006); // mem I/O access enable and bus master enable
|
|
|
+ {
|
|
|
+ uint32_t tmp = pci_read_config(dev_hdr->header.bus, dev_hdr->header.device, dev_hdr->header.func, 0x4);
|
|
|
+ tmp |= 0x6;
|
|
|
+ // mem I/O access enable and bus master enable
|
|
|
+ pci_write_config(dev_hdr->header.bus, dev_hdr->header.device, dev_hdr->header.func, 0x4, tmp);
|
|
|
+ }
|
|
|
io_mfence();
|
|
|
// 为当前控制器映射寄存器地址空间
|
|
|
xhci_hc[cid].vbase = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + XHCI_MAPPING_OFFSET + 65536 * xhci_hc[cid].controller_id;
|
|
@@ -883,7 +963,7 @@ void xhci_init(struct pci_device_structure_general_device_t *dev_hdr)
|
|
|
|
|
|
// kdebug("hcc1.xECP=%#010lx", hcc1.xECP);
|
|
|
// 计算operational registers的地址
|
|
|
- xhci_hc[cid].vbase_op = xhci_hc[cid].vbase + xhci_read_cap_reg8(cid, XHCI_CAPS_CAPLENGTH);
|
|
|
+ xhci_hc[cid].vbase_op = xhci_hc[cid].vbase + (xhci_read_cap_reg32(cid, XHCI_CAPS_CAPLENGTH) & 0xff);
|
|
|
io_mfence();
|
|
|
xhci_hc[cid].db_offset = xhci_read_cap_reg32(cid, XHCI_CAPS_DBOFF) & (~0x3); // bits [1:0] reserved
|
|
|
io_mfence();
|
|
@@ -894,13 +974,18 @@ void xhci_init(struct pci_device_structure_general_device_t *dev_hdr)
|
|
|
xhci_hc[cid].context_size = (hcc1.csz) ? 64 : 32;
|
|
|
|
|
|
if (iversion < 0x95)
|
|
|
- {
|
|
|
kwarn("Unsupported/Unknowned xHCI controller version: %#06x. This may cause unexpected behavior.", iversion);
|
|
|
- }
|
|
|
|
|
|
+ {
|
|
|
+
|
|
|
+ // Write to the FLADJ register incase the BIOS didn't
|
|
|
+ uint32_t tmp = pci_read_config(dev_hdr->header.bus, dev_hdr->header.device, dev_hdr->header.func, 0x60);
|
|
|
+ tmp |= (0x20 << 8);
|
|
|
+ pci_write_config(dev_hdr->header.bus, dev_hdr->header.device, dev_hdr->header.func, 0x60, tmp);
|
|
|
+ }
|
|
|
// if it is a Panther Point device, make sure sockets are xHCI controlled.
|
|
|
if (((pci_read_config(dev_hdr->header.bus, dev_hdr->header.device, dev_hdr->header.func, 0) & 0xffff) == 0x8086) &&
|
|
|
- ((pci_read_config(dev_hdr->header.bus, dev_hdr->header.device, dev_hdr->header.func, 2) & 0xffff) == 0x1E31) &&
|
|
|
+ (((pci_read_config(dev_hdr->header.bus, dev_hdr->header.device, dev_hdr->header.func, 0) >> 16) & 0xffff) == 0x1E31) &&
|
|
|
((pci_read_config(dev_hdr->header.bus, dev_hdr->header.device, dev_hdr->header.func, 8) & 0xff) == 4))
|
|
|
{
|
|
|
kdebug("Is a Panther Point device");
|
|
@@ -939,6 +1024,8 @@ void xhci_init(struct pci_device_structure_general_device_t *dev_hdr)
|
|
|
io_mfence();
|
|
|
// 创建command ring
|
|
|
xhci_hc[cid].cmd_ring_vaddr = xhci_create_ring(XHCI_CMND_RING_TRBS);
|
|
|
+ xhci_hc[cid].cmd_trb_vaddr = xhci_hc[cid].cmd_ring_vaddr;
|
|
|
+
|
|
|
if (unlikely(!xhci_is_aligned64(xhci_hc[cid].cmd_ring_vaddr))) // 地址不是按照64byte对齐
|
|
|
{
|
|
|
kerror("cmd ring isn't 64 byte aligned.");
|
|
@@ -962,10 +1049,22 @@ void xhci_init(struct pci_device_structure_general_device_t *dev_hdr)
|
|
|
|
|
|
FAIL_ON_TO(xhci_hc_init_intr(cid), failed_free_dyn);
|
|
|
io_mfence();
|
|
|
+
|
|
|
++xhci_ctrl_count;
|
|
|
io_mfence();
|
|
|
spin_unlock(&xhci_controller_init_lock);
|
|
|
io_mfence();
|
|
|
+
|
|
|
+ // 发送nop
|
|
|
+ struct xhci_TRB_normal_t nop_trb = {0};
|
|
|
+ nop_trb.cycle = xhci_hc[cid].cmd_trb_cycle;
|
|
|
+ nop_trb.TRB_type = TRB_TYPE_NO_OP;
|
|
|
+ // nop_trb.ioc = 1;
|
|
|
+ kdebug("to send nop TRB");
|
|
|
+ xhci_send_command(cid, &nop_trb, true);
|
|
|
+ xhci_send_command(cid, &nop_trb, true);
|
|
|
+ kdebug("nop TRB send OK");
|
|
|
+
|
|
|
return;
|
|
|
|
|
|
failed_free_dyn:; // 释放动态申请的内存
|