Skip to content

Commit 292ec6c

Browse files
shankerwangmiaoopsiff
authored andcommitted
loongarch: fix DMA address offset
Loongarch CPUs are using HT bus interface, which is only 40-bit wide, to communicate with the bridge chipset. However, the actual memory bus of the CPUs is 48-bit wide, and the available address space of DMA requests of PCI devices is also larger than the 40-bit address space. The address space of loongarch systems is not continuous, the bits [47:44] of which are used to denote the belonging node id, which is far beyond the space provided by the 40-bit wide HT bus. As a result, a translation on the both LS7A and CPU sides is needed. The translation happens on the LS7A side is controlled by the higher half of the register, HT_ROUTE. Bits [12:8] denotes dma_node_id_offset and bits[15:13] denotes dma_node_id_offset_mapped. The behavior of the translation is that the chip extracts the node id from bit dma_node_id_offset + 36 of a DMA address, places it at bit dma_node_id_offset_mapped + 32, and generates the address on the HT bus. On the CPU side, an alike translation happens, to convert the address on the HT bus back to a proper memory address. On machines with legacy firmware with BPI01000 version, dma_node_id_offset is configured with 0, resulting the address which should be used by the DMA engine of a PCI device differs from the actural physical memeory address, which requires a pair of arch-specific phys_to_dma and dma_to_phys functions or setting up the whole mapping in the _DMA method of the PCI root device in the DSDT table. The former method requires we add back the prevoiusly removed functions, and the latter method degrades the performance since when the translation happens, the mapping table is scanned linearly. This patch addresses this issue by directly setting dma_node_id_offset to 8, like what is done by modern firmwares, making the address used by the DMA engine just the same as the actual physical memory address, and eliminating the need of DMA address translation on the kernel side.
1 parent 797c053 commit 292ec6c

1 file changed

Lines changed: 27 additions & 0 deletions

File tree

arch/loongarch/kernel/legacy_boot.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,19 @@ enum bpi_mem_type {
4646
#define MSI_MSG_ADDRESS 0x2FF00000
4747
#define MSI_MSG_DEFAULT_COUNT 0xC0
4848

49+
/*
50+
``This should be configured by firmware"
51+
-- Section 4, Page 25 of Loongson-7A1000 User Manual v2.0
52+
but unable to figure out the exact value.
53+
The manual gives a typical value in Table 3.2, Page 23.
54+
*/
55+
#define LS7A_CHIPCFG_REG_OFF 0x00010000
56+
57+
/* Section 4.1, Page 26 of Loongson-7A1000 User Manual v2.0 */
58+
#define LS7A_DMA_CFG_OFF 0x041c
59+
#define LS7A_DMA_NODE_ID_OFFSET_SHF 8
60+
#define LS7A_DMA_NODE_ID_OFFSET_MASK GENMASK(12, 8)
61+
4962
struct loongarch_bpi_mem_map {
5063
struct loongarch_bpi_ext_hdr header; /*{"M", "E", "M"}*/
5164
u8 map_count;
@@ -588,6 +601,20 @@ static void __init init_acpi_arch_os_table_override (struct acpi_table_header *e
588601
new_madt->header.checksum = 0 - ext_listhdr_checksum((u8 *)new_madt, new_madt_size);
589602
new_mcfg->header.checksum = 0 - ext_listhdr_checksum((u8 *)new_mcfg, new_mcfg_size);
590603
*new_table = (struct acpi_table_header *)new_madt;
604+
605+
// Override LS7A dma_node_id_offset
606+
for(int i = 0; i < io_apic_count; i++){
607+
u64 ls7a_base_addr = bio_pics[i].address;
608+
void __iomem *dma_node_id_addr = (void __iomem *) TO_UNCACHE(ls7a_base_addr + LS7A_CHIPCFG_REG_OFF + LS7A_DMA_CFG_OFF);
609+
u32 dma_cfg = readl(dma_node_id_addr);
610+
u32 dma_node_id_offset = (dma_cfg & LS7A_DMA_NODE_ID_OFFSET_MASK) >> LS7A_DMA_NODE_ID_OFFSET_SHF;
611+
if(dma_node_id_offset != 8){
612+
pr_info("BPI: LS7A %d DMA node id offset is %d, will set to 8\n", i, dma_node_id_offset);
613+
dma_cfg &= ~LS7A_DMA_NODE_ID_OFFSET_MASK;
614+
dma_cfg |= 8 << LS7A_DMA_NODE_ID_OFFSET_SHF;
615+
writel(dma_cfg, dma_node_id_addr);
616+
}
617+
}
591618
}
592619

593620
void acpi_arch_os_table_override (struct acpi_table_header *existing_table, struct acpi_table_header **new_table){

0 commit comments

Comments
 (0)