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