18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 38c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 48c2ecf20Sopenharmony_ci * for more details. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2014 Kevin Cernekee <cernekee@gmail.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "bmips-dma: " fmt 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/device.h> 128c2ecf20Sopenharmony_ci#include <linux/dma-direction.h> 138c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/printk.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/types.h> 208c2ecf20Sopenharmony_ci#include <asm/bmips.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci * BCM338x has configurable address translation windows which allow the 248c2ecf20Sopenharmony_ci * peripherals' DMA addresses to be different from the Zephyr-visible 258c2ecf20Sopenharmony_ci * physical addresses. e.g. usb_dma_addr = zephyr_pa ^ 0x08000000 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * If the "brcm,ubus" node has a "dma-ranges" property we will enable this 288c2ecf20Sopenharmony_ci * translation globally using the provided information. This implements a 298c2ecf20Sopenharmony_ci * very limited subset of "dma-ranges" support and it will probably be 308c2ecf20Sopenharmony_ci * replaced by a more generic version later. 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct bmips_dma_range { 348c2ecf20Sopenharmony_ci u32 child_addr; 358c2ecf20Sopenharmony_ci u32 parent_addr; 368c2ecf20Sopenharmony_ci u32 size; 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic struct bmips_dma_range *bmips_dma_ranges; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define FLUSH_RAC 0x100 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cidma_addr_t phys_to_dma(struct device *dev, phys_addr_t pa) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct bmips_dma_range *r; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci for (r = bmips_dma_ranges; r && r->size; r++) { 488c2ecf20Sopenharmony_ci if (pa >= r->child_addr && 498c2ecf20Sopenharmony_ci pa < (r->child_addr + r->size)) 508c2ecf20Sopenharmony_ci return pa - r->child_addr + r->parent_addr; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci return pa; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ciphys_addr_t dma_to_phys(struct device *dev, dma_addr_t dma_addr) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct bmips_dma_range *r; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci for (r = bmips_dma_ranges; r && r->size; r++) { 608c2ecf20Sopenharmony_ci if (dma_addr >= r->parent_addr && 618c2ecf20Sopenharmony_ci dma_addr < (r->parent_addr + r->size)) 628c2ecf20Sopenharmony_ci return dma_addr - r->parent_addr + r->child_addr; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci return dma_addr; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cibool bmips_rac_flush_disable; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_civoid arch_sync_dma_for_cpu_all(void) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci void __iomem *cbr = BMIPS_GET_CBR(); 728c2ecf20Sopenharmony_ci u32 cfg; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (boot_cpu_type() != CPU_BMIPS3300 && 758c2ecf20Sopenharmony_ci boot_cpu_type() != CPU_BMIPS4350 && 768c2ecf20Sopenharmony_ci boot_cpu_type() != CPU_BMIPS4380) 778c2ecf20Sopenharmony_ci return; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (unlikely(bmips_rac_flush_disable)) 808c2ecf20Sopenharmony_ci return; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* Flush stale data out of the readahead cache */ 838c2ecf20Sopenharmony_ci cfg = __raw_readl(cbr + BMIPS_RAC_CONFIG); 848c2ecf20Sopenharmony_ci __raw_writel(cfg | 0x100, cbr + BMIPS_RAC_CONFIG); 858c2ecf20Sopenharmony_ci __raw_readl(cbr + BMIPS_RAC_CONFIG); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int __init bmips_init_dma_ranges(void) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct device_node *np = 918c2ecf20Sopenharmony_ci of_find_compatible_node(NULL, NULL, "brcm,ubus"); 928c2ecf20Sopenharmony_ci const __be32 *data; 938c2ecf20Sopenharmony_ci struct bmips_dma_range *r; 948c2ecf20Sopenharmony_ci int len; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (!np) 978c2ecf20Sopenharmony_ci return 0; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci data = of_get_property(np, "dma-ranges", &len); 1008c2ecf20Sopenharmony_ci if (!data) 1018c2ecf20Sopenharmony_ci goto out_good; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci len /= sizeof(*data) * 3; 1048c2ecf20Sopenharmony_ci if (!len) 1058c2ecf20Sopenharmony_ci goto out_bad; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* add a dummy (zero) entry at the end as a sentinel */ 1088c2ecf20Sopenharmony_ci bmips_dma_ranges = kcalloc(len + 1, sizeof(struct bmips_dma_range), 1098c2ecf20Sopenharmony_ci GFP_KERNEL); 1108c2ecf20Sopenharmony_ci if (!bmips_dma_ranges) 1118c2ecf20Sopenharmony_ci goto out_bad; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci for (r = bmips_dma_ranges; len; len--, r++) { 1148c2ecf20Sopenharmony_ci r->child_addr = be32_to_cpup(data++); 1158c2ecf20Sopenharmony_ci r->parent_addr = be32_to_cpup(data++); 1168c2ecf20Sopenharmony_ci r->size = be32_to_cpup(data++); 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ciout_good: 1208c2ecf20Sopenharmony_ci of_node_put(np); 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ciout_bad: 1248c2ecf20Sopenharmony_ci pr_err("error parsing dma-ranges property\n"); 1258c2ecf20Sopenharmony_ci of_node_put(np); 1268c2ecf20Sopenharmony_ci return -EINVAL; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ciarch_initcall(bmips_init_dma_ranges); 129