summaryrefslogtreecommitdiff
path: root/drivers/staging/fsl-mc/bus/fsl-mc-iommu.c
blob: d1778b205382a76ccd98bd7ca0d7aa03db1ca853 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/*
 * Copyright 2016 Freescale Semiconductor, Inc.
 * Copyright 2017 NXP
 * Author: Nipun Gupta <nipun.gupta@nxp.com>
 *
 * This file is licensed under the terms of the GNU General Public
 * License version 2. This program is licensed "as is" without any
 * warranty of any kind, whether express or implied.
 */

#include <linux/iommu.h>
#include <linux/of.h>
#include <linux/of_iommu.h>
#include "../include/mc.h"

/* Setup the IOMMU for the DPRC container */
static const struct iommu_ops
*fsl_mc_iommu_configure(struct fsl_mc_device *mc_dev,
	struct device_node *fsl_mc_platform_node)
{
	struct of_phandle_args iommu_spec;
	const struct iommu_ops *ops;
	u32 iommu_phandle;
	struct device_node *iommu_node;
	const __be32 *map = NULL;
	int iommu_cells, map_len, ret;

	map = of_get_property(fsl_mc_platform_node, "iommu-map", &map_len);
	if (!map)
		return NULL;

	ops = mc_dev->dev.bus->iommu_ops;
	if (!ops || !ops->of_xlate)
		return NULL;

	iommu_phandle = be32_to_cpup(map + 1);
	iommu_node = of_find_node_by_phandle(iommu_phandle);

	if (of_property_read_u32(iommu_node, "#iommu-cells", &iommu_cells)) {
		pr_err("%s: missing #iommu-cells property\n", iommu_node->name);
		return NULL;
	}

	/* Initialize the fwspec */
	ret = iommu_fwspec_init(&mc_dev->dev, &iommu_node->fwnode, ops);
	if (ret)
		return NULL;

	/*
	 * Fill in the required stream-id before calling the iommu's
	 * ops->xlate callback.
	 */
	iommu_spec.np = iommu_node;
	iommu_spec.args[0] = mc_dev->icid;
	iommu_spec.args_count = 1;

	ret = ops->of_xlate(&mc_dev->dev, &iommu_spec);
	if (ret)
		return NULL;

	of_node_put(iommu_spec.np);

	return ops;
}

/* Set up DMA configuration for fsl-mc devices */
void fsl_mc_dma_configure(struct fsl_mc_device *mc_dev,
	struct device_node *fsl_mc_platform_node, int coherent)
{
	const struct iommu_ops *ops;

	ops = fsl_mc_iommu_configure(mc_dev, fsl_mc_platform_node);

	mc_dev->dev.coherent_dma_mask = DMA_BIT_MASK(48);
	mc_dev->dev.dma_mask = &mc_dev->dev.coherent_dma_mask;
	arch_setup_dma_ops(&mc_dev->dev, 0,
		mc_dev->dev.coherent_dma_mask + 1, ops, coherent);
}

/* Macro to get the container device of a MC device */
#define fsl_mc_cont_dev(_dev) ((to_fsl_mc_device(_dev)->flags & \
	FSL_MC_IS_DPRC) ? (_dev) : ((_dev)->parent))

/* Macro to check if a device is a container device */
#define is_cont_dev(_dev) (to_fsl_mc_device(_dev)->flags & FSL_MC_IS_DPRC)

/* Get the IOMMU group for device on fsl-mc bus */
struct iommu_group *fsl_mc_device_group(struct device *dev)
{
	struct device *cont_dev = fsl_mc_cont_dev(dev);
	struct iommu_group *group;

	/* Container device is responsible for creating the iommu group */
	if (is_cont_dev(dev)) {
		group = iommu_group_alloc();
		if (IS_ERR(group))
			return NULL;
	} else {
		get_device(cont_dev);
		group = iommu_group_get(cont_dev);
		put_device(cont_dev);
	}

	return group;
}