diff --git a/Documentation/devicetree/bindings/arm/coherency-fabric.txt b/Documentation/devicetree/bindings/arm/coherency-fabric.txt index 2bfbf67dd77e..17d8cd107559 100644 --- a/Documentation/devicetree/bindings/arm/coherency-fabric.txt +++ b/Documentation/devicetree/bindings/arm/coherency-fabric.txt @@ -5,12 +5,17 @@ Available on Marvell SOCs: Armada 370 and Armada XP Required properties: - compatible: "marvell,coherency-fabric" -- reg: Should contain,coherency fabric registers location and length. + +- reg: Should contain coherency fabric registers location and + length. First pair for the coherency fabric registers, second pair + for the per-CPU fabric registers registers. Example: coherency-fabric@d0020200 { compatible = "marvell,coherency-fabric"; - reg = <0xd0020200 0xb0>; + reg = <0xd0020200 0xb0>, + <0xd0021810 0x1c>; + }; diff --git a/arch/arm/boot/dts/armada-370-xp.dtsi b/arch/arm/boot/dts/armada-370-xp.dtsi index af7d16e5a024..9f43d0efe5d9 100644 --- a/arch/arm/boot/dts/armada-370-xp.dtsi +++ b/arch/arm/boot/dts/armada-370-xp.dtsi @@ -38,7 +38,8 @@ coherency-fabric@d0020200 { compatible = "marvell,coherency-fabric"; - reg = <0xd0020200 0xb0>; + reg = <0xd0020200 0xb0>, + <0xd0021810 0x1c>; }; soc { diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index 23004847bb05..98d4dabb2c10 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -111,6 +111,8 @@ static inline void dma_free_noncoherent(struct device *dev, size_t size, extern int dma_supported(struct device *dev, u64 mask); +extern int arm_dma_set_mask(struct device *dev, u64 dma_mask); + /** * arm_dma_alloc - allocate consistent memory for DMA * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices diff --git a/arch/arm/mach-mvebu/addr-map.c b/arch/arm/mach-mvebu/addr-map.c index fe454a4430be..595f6b722a8f 100644 --- a/arch/arm/mach-mvebu/addr-map.c +++ b/arch/arm/mach-mvebu/addr-map.c @@ -108,6 +108,9 @@ static int __init armada_setup_cpu_mbus(void) addr_map_cfg.bridge_virt_base = mbus_unit_addr_decoding_base; + if (of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric")) + addr_map_cfg.hw_io_coherency = 1; + /* * Disable, clear and configure windows. */ diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c index 596ee66a9cc4..8278960066c3 100644 --- a/arch/arm/mach-mvebu/coherency.c +++ b/arch/arm/mach-mvebu/coherency.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include "armada-370-xp.h" @@ -33,10 +35,13 @@ * value matching its virtual mapping */ static void __iomem *coherency_base = ARMADA_370_XP_REGS_VIRT_BASE + 0x20200; +static void __iomem *coherency_cpu_base; /* Coherency fabric registers */ #define COHERENCY_FABRIC_CFG_OFFSET 0x4 +#define IO_SYNC_BARRIER_CTL_OFFSET 0x0 + static struct of_device_id of_coherency_table[] = { {.compatible = "marvell,coherency-fabric"}, { /* end of list */ }, @@ -68,6 +73,70 @@ int set_cpu_coherent(unsigned int hw_cpu_id, int smp_group_id) return ll_set_cpu_coherent(coherency_base, hw_cpu_id); } +static inline void mvebu_hwcc_sync_io_barrier(void) +{ + writel(0x1, coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET); + while (readl(coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET) & 0x1); +} + +static dma_addr_t mvebu_hwcc_dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + if (dir != DMA_TO_DEVICE) + mvebu_hwcc_sync_io_barrier(); + return pfn_to_dma(dev, page_to_pfn(page)) + offset; +} + + +static void mvebu_hwcc_dma_unmap_page(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + if (dir != DMA_TO_DEVICE) + mvebu_hwcc_sync_io_barrier(); +} + +static void mvebu_hwcc_dma_sync(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction dir) +{ + if (dir != DMA_TO_DEVICE) + mvebu_hwcc_sync_io_barrier(); +} + +static struct dma_map_ops mvebu_hwcc_dma_ops = { + .alloc = arm_dma_alloc, + .free = arm_dma_free, + .mmap = arm_dma_mmap, + .map_page = mvebu_hwcc_dma_map_page, + .unmap_page = mvebu_hwcc_dma_unmap_page, + .get_sgtable = arm_dma_get_sgtable, + .map_sg = arm_dma_map_sg, + .unmap_sg = arm_dma_unmap_sg, + .sync_single_for_cpu = mvebu_hwcc_dma_sync, + .sync_single_for_device = mvebu_hwcc_dma_sync, + .sync_sg_for_cpu = arm_dma_sync_sg_for_cpu, + .sync_sg_for_device = arm_dma_sync_sg_for_device, + .set_dma_mask = arm_dma_set_mask, +}; + +static int mvebu_hwcc_platform_notifier(struct notifier_block *nb, + unsigned long event, void *__dev) +{ + struct device *dev = __dev; + + if (event != BUS_NOTIFY_ADD_DEVICE) + return NOTIFY_DONE; + set_dma_ops(dev, &mvebu_hwcc_dma_ops); + + return NOTIFY_OK; +} + +static struct notifier_block mvebu_hwcc_platform_nb = { + .notifier_call = mvebu_hwcc_platform_notifier, +}; + int __init coherency_init(void) { struct device_node *np; @@ -76,6 +145,10 @@ int __init coherency_init(void) if (np) { pr_info("Initializing Coherency fabric\n"); coherency_base = of_iomap(np, 0); + coherency_cpu_base = of_iomap(np, 1); + set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0); + bus_register_notifier(&platform_bus_type, + &mvebu_hwcc_platform_nb); } return 0; diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 58bc3e4d3bd0..5383bc018571 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -124,8 +124,6 @@ static void arm_dma_sync_single_for_device(struct device *dev, __dma_page_cpu_to_dev(page, offset, size, dir); } -static int arm_dma_set_mask(struct device *dev, u64 dma_mask); - struct dma_map_ops arm_dma_ops = { .alloc = arm_dma_alloc, .free = arm_dma_free, @@ -971,7 +969,7 @@ int dma_supported(struct device *dev, u64 mask) } EXPORT_SYMBOL(dma_supported); -static int arm_dma_set_mask(struct device *dev, u64 dma_mask) +int arm_dma_set_mask(struct device *dev, u64 dma_mask) { if (!dev->dma_mask || !dma_supported(dev, dma_mask)) return -EIO; diff --git a/arch/arm/plat-orion/addr-map.c b/arch/arm/plat-orion/addr-map.c index a7b8060c293a..febe3862873c 100644 --- a/arch/arm/plat-orion/addr-map.c +++ b/arch/arm/plat-orion/addr-map.c @@ -42,6 +42,8 @@ EXPORT_SYMBOL_GPL(mv_mbus_dram_info); #define WIN_REMAP_LO_OFF 0x0008 #define WIN_REMAP_HI_OFF 0x000c +#define ATTR_HW_COHERENCY (0x1 << 4) + /* * Default implementation */ @@ -163,6 +165,8 @@ void __init orion_setup_cpu_mbus_target(const struct orion_addr_map_cfg *cfg, w = &orion_mbus_dram_info.cs[cs++]; w->cs_index = i; w->mbus_attr = 0xf & ~(1 << i); + if (cfg->hw_io_coherency) + w->mbus_attr |= ATTR_HW_COHERENCY; w->base = base & 0xffff0000; w->size = (size | 0x0000ffff) + 1; } diff --git a/arch/arm/plat-orion/include/plat/addr-map.h b/arch/arm/plat-orion/include/plat/addr-map.h index ec63e4a627d0..b76c06569fe5 100644 --- a/arch/arm/plat-orion/include/plat/addr-map.h +++ b/arch/arm/plat-orion/include/plat/addr-map.h @@ -17,6 +17,7 @@ struct orion_addr_map_cfg { const int num_wins; /* Total number of windows */ const int remappable_wins; void __iomem *bridge_virt_base; + int hw_io_coherency; /* If NULL, the default cpu_win_can_remap will be used, using the value in remappable_wins */