diff mbox series

[v2,2/3] MIPS: Loongson: Add DMA support for 7A1000

Message ID 1584932355-3642-3-git-send-email-yangtiezhu@loongson.cn (mailing list archive)
State Superseded
Headers show
Series Add basic support for Loongson 7A1000 bridge chip | expand

Commit Message

Tiezhu Yang March 23, 2020, 2:59 a.m. UTC
Implement __phys_to_dma() and __dma_to_phys() according to the
node id offset in the 7A1000 DMA route config register.

Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
---
 arch/mips/include/asm/mach-loongson64/boot_param.h |  5 +++++
 arch/mips/loongson64/dma.c                         |  9 ++++++---
 arch/mips/loongson64/env.c                         |  2 ++
 arch/mips/loongson64/init.c                        | 17 +++++++++++++++++
 4 files changed, 30 insertions(+), 3 deletions(-)

Comments

Christoph Hellwig March 25, 2020, 1:27 p.m. UTC | #1
On Mon, Mar 23, 2020 at 10:59:14AM +0800, Tiezhu Yang wrote:
> Implement __phys_to_dma() and __dma_to_phys() according to the
> node id offset in the 7A1000 DMA route config register.

Can you please try to just use the dma_pfn_offset field in struct device
for all loongson platforms?  I'm pretty sure I asked for that last time
around..
Tiezhu Yang March 26, 2020, 2:44 a.m. UTC | #2
On 03/25/2020 09:27 PM, Christoph Hellwig wrote:
> On Mon, Mar 23, 2020 at 10:59:14AM +0800, Tiezhu Yang wrote:
>> Implement __phys_to_dma() and __dma_to_phys() according to the
>> node id offset in the 7A1000 DMA route config register.
> Can you please try to just use the dma_pfn_offset field in struct device
> for all loongson platforms?  I'm pretty sure I asked for that last time
> around..

Oh, sorry, I don't quite understand what you mean last time.

Do you mean use the default implementation in include/linux/dma-direct.h
when CONFIG_ARCH_HAS_PHYS_TO_DMA is not set?

#ifdef CONFIG_ARCH_HAS_PHYS_TO_DMA
#include <asm/dma-direct.h>
#else
static inline dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t 
paddr)
{
         dma_addr_t dev_addr = (dma_addr_t)paddr;

         return dev_addr - ((dma_addr_t)dev->dma_pfn_offset << PAGE_SHIFT);
}

static inline phys_addr_t __dma_to_phys(struct device *dev, dma_addr_t 
dev_addr)
{
         phys_addr_t paddr = (phys_addr_t)dev_addr;

         return paddr + ((phys_addr_t)dev->dma_pfn_offset << PAGE_SHIFT);
}
#endif /* !CONFIG_ARCH_HAS_PHYS_TO_DMA */

If so, I will verify it used with the AMD RS780E and Loongson 7A1000 
bridge chip.

Thanks,

Tiezhu Yang
Jiaxun Yang March 27, 2020, 6:26 a.m. UTC | #3
于 2020年3月25日 GMT+08:00 下午9:27:56, Christoph Hellwig <hch@infradead.org> 写到:
>On Mon, Mar 23, 2020 at 10:59:14AM +0800, Tiezhu Yang wrote:
>> Implement __phys_to_dma() and __dma_to_phys() according to the
>> node id offset in the 7A1000 DMA route config register.
>
>Can you please try to just use the dma_pfn_offset field in struct
>device
>for all loongson platforms?  I'm pretty sure I asked for that last time
>around..

Here we have a problem that dma_pfn_offset doesn't fit our platform.

We have multiple dma-ranges that translate in to different address and
dma_pfn_offset can only handle single range 
with fixed offset.

Thanks.
Tiezhu Yang March 31, 2020, 9:58 a.m. UTC | #4
On 03/25/2020 09:27 PM, Christoph Hellwig wrote:
> On Mon, Mar 23, 2020 at 10:59:14AM +0800, Tiezhu Yang wrote:
>> Implement __phys_to_dma() and __dma_to_phys() according to the
>> node id offset in the 7A1000 DMA route config register.
> Can you please try to just use the dma_pfn_offset field in struct device
> for all loongson platforms?  I'm pretty sure I asked for that last time
> around..

Hi Christoph,

In the current market, the most used bridge chip on the Loongson platform
are AMD RS780E and Loongson 7A1000, the AMD RS780E is already supported by
the mainline kernel.

If use the default implementation of __phys_to_dma() and __dma_to_phys()
in dma-direct.h when CONFIG_ARCH_HAS_PHYS_TO_DMA is not set, it works
well used with 7A1000 on the Loongson single-way and multi-way platform,
and also works well used with RS780E on the Loongson single-way platform,
but the DMA address will be wrong on the non-node0 used with RS780E on
the Loongson multi-way platform.

Just as the description in the code comment, the devices get node id from
40 bit of HyperTransport bus, so we extract 2 bit node id (bit 44~45) from
48 bit address space of Loongson CPU and embed it into HyperTransport bus
(bit 37-38), this operation can be done only at the software level used
with RS780E on the Loongson multi-way platform, because it has no hardware
function to translate address of node id, this is a hardware compatibility
problem.

Device
     |
     | DMA address
     |
Host Bridge
     |
     | HT bus address (40 bit)
     |
    CPU
     |
     | physical address (48 bit)
     |
    RAM

The Loongson 7A1000 has dma_node_id_offset field in the DMA route config
register, the hardware can use the dma_node_id_offset to translate address
of node id automatically, so we can get correct address when just use the
dma_pfn_offset field in struct device.

For the above reasons, in order to maintain downward compatibility to
support the AMD RS780E bridge chip, it is better to use the platform
dependent implementation of __phys_to_dma() and __dma_to_phys().

Thanks,

Tiezhu Yang
diff mbox series

Patch

diff --git a/arch/mips/include/asm/mach-loongson64/boot_param.h b/arch/mips/include/asm/mach-loongson64/boot_param.h
index 5e8c70d..c759b7c 100644
--- a/arch/mips/include/asm/mach-loongson64/boot_param.h
+++ b/arch/mips/include/asm/mach-loongson64/boot_param.h
@@ -219,9 +219,14 @@  struct loongson_system_configuration {
 	u32 nr_sensors;
 	struct sensor_device sensors[MAX_SENSORS];
 	u64 workarounds;
+	void (*early_config)(void);
 };
 
 extern struct efi_memory_map_loongson *loongson_memmap;
 extern struct loongson_system_configuration loongson_sysconf;
 
+extern u32 node_id_offset;
+extern void rs780e_early_config(void);
+extern void ls7a1000_early_config(void);
+
 #endif
diff --git a/arch/mips/loongson64/dma.c b/arch/mips/loongson64/dma.c
index 5e86635..dbfe6e8 100644
--- a/arch/mips/loongson64/dma.c
+++ b/arch/mips/loongson64/dma.c
@@ -2,21 +2,24 @@ 
 #include <linux/dma-direct.h>
 #include <linux/init.h>
 #include <linux/swiotlb.h>
+#include <boot_param.h>
 
 dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr)
 {
 	/* We extract 2bit node id (bit 44~47, only bit 44~45 used now) from
 	 * Loongson-3's 48bit address space and embed it into 40bit */
 	long nid = (paddr >> 44) & 0x3;
-	return ((nid << 44) ^ paddr) | (nid << 37);
+
+	return ((nid << 44) ^ paddr) | (nid << node_id_offset);
 }
 
 phys_addr_t __dma_to_phys(struct device *dev, dma_addr_t daddr)
 {
 	/* We extract 2bit node id (bit 44~47, only bit 44~45 used now) from
 	 * Loongson-3's 48bit address space and embed it into 40bit */
-	long nid = (daddr >> 37) & 0x3;
-	return ((nid << 37) ^ daddr) | (nid << 44);
+	long nid = (daddr >> node_id_offset) & 0x3;
+
+	return ((nid << node_id_offset) ^ daddr) | (nid << 44);
 }
 
 void __init plat_swiotlb_setup(void)
diff --git a/arch/mips/loongson64/env.c b/arch/mips/loongson64/env.c
index 42542c7..32a3822 100644
--- a/arch/mips/loongson64/env.c
+++ b/arch/mips/loongson64/env.c
@@ -167,8 +167,10 @@  void __init prom_init_env(void)
 	if (vendor == 0x0014 && device == 0x7a00) {
 		pr_info("The bridge chip is Loongson 7A1000\n");
 		loongson_sysconf.bridgetype = LS7A1000;
+		loongson_sysconf.early_config = ls7a1000_early_config;
 	} else {
 		pr_info("The bridge chip is AMD RS780E or SR5690\n");
 		loongson_sysconf.bridgetype = RS780E;
+		loongson_sysconf.early_config = rs780e_early_config;
 	}
 }
diff --git a/arch/mips/loongson64/init.c b/arch/mips/loongson64/init.c
index 5ac1a0f..0b6493b 100644
--- a/arch/mips/loongson64/init.c
+++ b/arch/mips/loongson64/init.c
@@ -12,6 +12,11 @@ 
 #include <asm/fw/fw.h>
 
 #include <loongson.h>
+#include <boot_param.h>
+
+#define NODE_ID_OFFSET_ADDR     0x90000e001001041c
+
+u32 node_id_offset;
 
 static void __init mips_nmi_setup(void)
 {
@@ -23,6 +28,16 @@  static void __init mips_nmi_setup(void)
 	flush_icache_range((unsigned long)base, (unsigned long)base + 0x80);
 }
 
+void rs780e_early_config(void)
+{
+	node_id_offset = 37;
+}
+
+void ls7a1000_early_config(void)
+{
+	node_id_offset = ((readl((u32 *)NODE_ID_OFFSET_ADDR) >> 8) & 0x1f) + 36;
+}
+
 void __init prom_init(void)
 {
 	fw_init_cmdline();
@@ -32,6 +47,8 @@  void __init prom_init(void)
 	set_io_port_base((unsigned long)
 		ioremap(LOONGSON_PCIIO_BASE, LOONGSON_PCIIO_SIZE));
 
+	loongson_sysconf.early_config();
+
 	prom_init_numa_memory();
 
 	/* Hardcode to CPU UART 0 */