mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-10 12:16:48 +00:00
bugfix: 修复了无法收到xhci控制器中断的bug
This commit is contained in:
parent
ff94280f77
commit
e22fe35bb1
@ -14,7 +14,7 @@
|
||||
|
||||
/**
|
||||
* @brief 生成msi消息
|
||||
*
|
||||
*
|
||||
* @param msi_desc msi描述符
|
||||
* @return struct msi_msg_t* msi消息指针(在描述符内)
|
||||
*/
|
||||
@ -23,5 +23,6 @@ struct msi_msg_t *msi_arch_get_msg(struct msi_desc_t *msi_desc)
|
||||
msi_desc->msg.address_hi = 0;
|
||||
msi_desc->msg.address_lo = ia64_pci_get_arch_msi_message_address(msi_desc->processor);
|
||||
msi_desc->msg.data = ia64_pci_get_arch_msi_message_data(msi_desc->irq_num, msi_desc->processor, msi_desc->edge_trigger, msi_desc->assert);
|
||||
msi_desc->msg.vector_control = 0;
|
||||
return &(msi_desc->msg);
|
||||
}
|
@ -23,6 +23,7 @@ static __always_inline struct pci_msix_cap_t __msi_read_msix_cap_list(struct msi
|
||||
struct pci_msix_cap_t cap_list = {0};
|
||||
uint32_t dw0;
|
||||
dw0 = pci_read_config(msi_desc->pci_dev->bus, msi_desc->pci_dev->device, msi_desc->pci_dev->func, cap_off);
|
||||
io_lfence();
|
||||
cap_list.cap_id = dw0 & 0xff;
|
||||
cap_list.next_off = (dw0 >> 8) & 0xff;
|
||||
cap_list.msg_ctrl = (dw0 >> 16) & 0xffff;
|
||||
@ -82,7 +83,7 @@ static __always_inline int __msix_map_table(struct pci_device_structure_header_t
|
||||
mmio_create(pci_dev->msix_mmio_size, VM_IO | VM_DONTCOPY, &pci_dev->msix_mmio_vaddr, &pci_dev->msix_mmio_size);
|
||||
pci_dev->msix_mmio_vaddr &= (~0xf);
|
||||
uint32_t bar = pci_read_config(pci_dev->bus, pci_dev->device, pci_dev->func, bar_off);
|
||||
kdebug("pci_dev->msix_mmio_vaddr=%#018lx, bar=%#010lx, table offset=%#010lx, table_size=%#010lx, mmio_size=%d", pci_dev->msix_mmio_vaddr, bar, pci_dev->msix_offset, pci_dev->msix_table_size, pci_dev->msix_mmio_size);
|
||||
// kdebug("pci_dev->msix_mmio_vaddr=%#018lx, bar=%#010lx, table offset=%#010lx, table_size=%#010lx, mmio_size=%d", pci_dev->msix_mmio_vaddr, bar, pci_dev->msix_offset, pci_dev->msix_table_size, pci_dev->msix_mmio_size);
|
||||
|
||||
// 将msix table映射到页表
|
||||
mm_map(&initial_mm, pci_dev->msix_mmio_vaddr, pci_dev->msix_mmio_size, bar);
|
||||
@ -98,16 +99,19 @@ static __always_inline int __msix_map_table(struct pci_device_structure_header_t
|
||||
static __always_inline void __msix_set_entry(struct msi_desc_t *msi_desc)
|
||||
{
|
||||
uint64_t *ptr = (uint64_t *)(msi_desc->pci_dev->msix_mmio_vaddr + msi_desc->pci_dev->msix_offset + msi_desc->msi_index * 16);
|
||||
*ptr = (msi_desc->msg.address_hi << 32) | (msi_desc->msg.address_lo);
|
||||
*ptr = ((uint64_t)(msi_desc->msg.address_hi) << 32) | (msi_desc->msg.address_lo);
|
||||
io_mfence();
|
||||
++ptr;
|
||||
*ptr = (msi_desc->msg.vector_control << 32) | (msi_desc->msg.data);
|
||||
io_mfence();
|
||||
*ptr = ((uint64_t)(msi_desc->msg.vector_control) << 32) | (msi_desc->msg.data);
|
||||
io_mfence();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清空设备的msix table的指定表项
|
||||
*
|
||||
*
|
||||
* @param pci_dev pci设备
|
||||
* @param msi_index 表项号
|
||||
* @param msi_index 表项号
|
||||
*/
|
||||
static __always_inline void __msix_clear_entry(struct pci_device_structure_header_t *pci_dev, uint16_t msi_index)
|
||||
{
|
||||
@ -164,13 +168,15 @@ int pci_enable_msi(struct msi_desc_t *msi_desc)
|
||||
struct pci_msix_cap_t cap = __msi_read_msix_cap_list(msi_desc, cap_ptr);
|
||||
// 映射msix table
|
||||
__msix_map_table(msi_desc->pci_dev, &cap);
|
||||
|
||||
io_mfence();
|
||||
// 设置msix的中断
|
||||
__msix_set_entry(msi_desc);
|
||||
io_mfence();
|
||||
|
||||
// 使能msi
|
||||
// todo: disable intx
|
||||
// 使能msi-x
|
||||
tmp = pci_read_config(ptr->bus, ptr->device, ptr->func, cap_ptr); // 读取cap+0x0处的值
|
||||
tmp |= (1 << 16);
|
||||
tmp |= (1 << 31);
|
||||
pci_write_config(ptr->bus, ptr->device, ptr->func, cap_ptr, tmp);
|
||||
}
|
||||
else
|
||||
|
@ -70,6 +70,7 @@ uint pci_write_config(uchar bus, uchar slot, uchar func, uchar offset, uint32_t
|
||||
// 构造pci配置空间地址
|
||||
uint address = (uint)((lbus << 16) | (lslot << 11) | (lfunc << 8) | (offset & 0xfc) | ((uint)0x80000000));
|
||||
io_out32(PORT_PCI_CONFIG_ADDRESS, address);
|
||||
|
||||
// 写入数据
|
||||
io_out32(PORT_PCI_CONFIG_DATA, data);
|
||||
|
||||
|
@ -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:; // 释放动态申请的内存
|
||||
|
@ -390,6 +390,7 @@ struct xhci_host_controller_t
|
||||
uint32_t page_size; // page size
|
||||
uint64_t dcbaap_vaddr; // Device Context Base Address Array Pointer的虚拟地址
|
||||
uint64_t cmd_ring_vaddr; // command ring的虚拟地址
|
||||
uint64_t cmd_trb_vaddr; // 下一个要写入的trb的虚拟地址
|
||||
uint64_t event_ring_vaddr; // event ring的虚拟地址
|
||||
uint64_t event_ring_table_vaddr; // event ring table的虚拟地址
|
||||
uint8_t cmd_trb_cycle; // 当前command ring cycle
|
||||
|
Loading…
x
Reference in New Issue
Block a user