drm/msm: Refactor GPU IOMMU

Very soon we will be adding support for secure domains and so a bit of
refactoring is needed the GPU IOMMU code:

 * Add support for directly probing the context bank device at create
instead of at attach. This makes it a little bit easier to directly
associate a mmu device with a specific context bank.

 * Specify the domain type at create time. Add a new domain type
MSM_DOMAIN_USER to associate the user domain with the gfx3d_user
context bank.  Also add MSM_DOMAIN_DEFAULT with no context bank
for legacy devices (read MDP4) with only one context bank
to attach to the parent device. Adding a domain type saves us from
having to create N entry points for each domain type.

Note that dynamic domains stay with their own initalization function.
This is because dynamic domains are cloned from the parent domain
so the semantics are too different to try to smash into the generic
functions.

Change-Id: Ic0dedbad41692e776cddc72cda653ae637f9ec77
Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
This commit is contained in:
Jordan Crouse 2017-04-07 15:01:41 -06:00
parent 1576b22ae9
commit 4e061d8b60
6 changed files with 129 additions and 88 deletions

View file

@ -515,15 +515,11 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev)
mdelay(16);
if (config->iommu) {
struct msm_mmu *mmu = msm_iommu_new(&pdev->dev, config->iommu);
if (IS_ERR(mmu)) {
ret = PTR_ERR(mmu);
goto fail;
}
config->iommu->geometry.aperture_start = 0x1000;
config->iommu->geometry.aperture_end = 0xffffffff;
aspace = msm_gem_address_space_create(&pdev->dev,
mmu, "mdp4", 0x1000, 0xffffffff);
config->iommu, MSM_IOMMU_DOMAIN_DEFAULT, "mdp4");
if (IS_ERR(aspace)) {
ret = PTR_ERR(aspace);
goto fail;

View file

@ -412,7 +412,7 @@ void msm_gem_address_space_put(struct msm_gem_address_space *aspace);
/* For GPU and legacy display */
struct msm_gem_address_space *
msm_gem_address_space_create(struct device *dev, struct iommu_domain *domain,
const char *name);
int type, const char *name);
struct msm_gem_address_space *
msm_gem_address_space_create_instance(struct msm_mmu *parent, const char *name,
uint64_t start, uint64_t end);

View file

@ -147,9 +147,9 @@ msm_gem_smmu_address_space_create(struct device *dev, struct msm_mmu *mmu,
struct msm_gem_address_space *
msm_gem_address_space_create(struct device *dev, struct iommu_domain *domain,
const char *name)
int type, const char *name)
{
struct msm_mmu *mmu = msm_iommu_new(dev, domain);
struct msm_mmu *mmu = msm_iommu_new(dev, type, domain);
if (IS_ERR(mmu))
return (struct msm_gem_address_space *) mmu;

View file

@ -837,7 +837,7 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
dev_info(drm->dev, "%s: using IOMMU\n", name);
gpu->aspace = msm_gem_address_space_create(&pdev->dev,
iommu, "gpu");
iommu, MSM_IOMMU_DOMAIN_USER, "gpu");
if (IS_ERR(gpu->aspace)) {
ret = PTR_ERR(gpu->aspace);
dev_err(drm->dev, "failed to init iommu: %d\n", ret);

View file

@ -27,23 +27,41 @@ static int msm_fault_handler(struct iommu_domain *iommu, struct device *dev,
return 0;
}
static void enable_iommu_clocks(struct msm_iommu *iommu)
{
int i;
for (i = 0; i < iommu->nr_clocks; i++) {
if (iommu->clocks[i])
clk_prepare_enable(iommu->clocks[i]);
}
}
static void disable_iommu_clocks(struct msm_iommu *iommu)
{
int i;
for (i = 0; i < iommu->nr_clocks; i++) {
if (iommu->clocks[i])
clk_disable_unprepare(iommu->clocks[i]);
}
}
/*
* Get and enable the IOMMU clocks so that we can make
* sure they stay on the entire duration so that we can
* safely change the pagetable from the GPU
*/
static void _get_iommu_clocks(struct msm_mmu *mmu, struct platform_device *pdev)
static void get_iommu_clocks(struct msm_iommu *iommu, struct device *dev)
{
struct msm_iommu *iommu = to_msm_iommu(mmu);
struct device *dev;
struct property *prop;
const char *name;
int i = 0;
if (WARN_ON(!pdev))
if (iommu->nr_clocks) {
enable_iommu_clocks(iommu);
return;
dev = &pdev->dev;
}
iommu->nr_clocks =
of_property_count_strings(dev->of_node, "clock-names");
@ -61,79 +79,40 @@ static void _get_iommu_clocks(struct msm_mmu *mmu, struct platform_device *pdev)
break;
iommu->clocks[i] = clk_get(dev, name);
if (iommu->clocks[i])
clk_prepare_enable(iommu->clocks[i]);
i++;
}
enable_iommu_clocks(iommu);
}
static int _attach_iommu_device(struct msm_mmu *mmu,
struct iommu_domain *domain, const char **names, int cnt)
{
int i;
/* See if there is a iommus member in the current device. If not, look
* for the names and see if there is one in there.
*/
if (of_find_property(mmu->dev->of_node, "iommus", NULL))
return iommu_attach_device(domain, mmu->dev);
/* Look through the list of names for a target */
for (i = 0; i < cnt; i++) {
struct device_node *node =
of_find_node_by_name(mmu->dev->of_node, names[i]);
if (!node)
continue;
if (of_find_property(node, "iommus", NULL)) {
struct platform_device *pdev;
/* Get the platform device for the node */
of_platform_populate(node->parent, NULL, NULL,
mmu->dev);
pdev = of_find_device_by_node(node);
if (!pdev)
continue;
_get_iommu_clocks(mmu,
of_find_device_by_node(node->parent));
mmu->dev = &pdev->dev;
return iommu_attach_device(domain, mmu->dev);
}
}
dev_err(mmu->dev, "Couldn't find a IOMMU device\n");
return -ENODEV;
}
static int msm_iommu_attach(struct msm_mmu *mmu, const char **names, int cnt)
static int msm_iommu_attach(struct msm_mmu *mmu, const char **names,
int cnt)
{
struct msm_iommu *iommu = to_msm_iommu(mmu);
int val = 1, ret;
return iommu_attach_device(iommu->domain, mmu->dev);
}
static int msm_iommu_attach_user(struct msm_mmu *mmu, const char **names,
int cnt)
{
struct msm_iommu *iommu = to_msm_iommu(mmu);
int ret, val = 1;
/* Hope springs eternal */
iommu->allow_dynamic = true;
iommu->allow_dynamic = !iommu_domain_set_attr(iommu->domain,
DOMAIN_ATTR_ENABLE_TTBR1, &val) ? true : false;
/* per-instance pagetables need TTBR1 support in the IOMMU driver */
ret = iommu_domain_set_attr(iommu->domain,
DOMAIN_ATTR_ENABLE_TTBR1, &val);
if (ret)
iommu->allow_dynamic = false;
get_iommu_clocks(iommu, mmu->dev->parent);
/* Mark the GPU as I/O coherent if it is supported */
iommu->is_coherent = of_dma_is_coherent(mmu->dev->of_node);
/* Attach the device to the domain */
ret = _attach_iommu_device(mmu, iommu->domain, names, cnt);
if (ret)
ret = iommu_attach_device(iommu->domain, mmu->dev);
if (ret) {
disable_iommu_clocks(iommu);
return ret;
}
/*
* Get the context bank for the base domain; this will be shared with
@ -179,14 +158,10 @@ static int msm_iommu_attach_dynamic(struct msm_mmu *mmu, const char **names,
static void msm_iommu_detach(struct msm_mmu *mmu)
{
struct msm_iommu *iommu = to_msm_iommu(mmu);
int i;
iommu_detach_device(iommu->domain, mmu->dev);
for (i = 0; i < iommu->nr_clocks; i++) {
if (iommu->clocks[i])
clk_disable(iommu->clocks[i]);
}
disable_iommu_clocks(iommu);
}
static void msm_iommu_detach_dynamic(struct msm_mmu *mmu)
@ -249,7 +224,30 @@ static void msm_iommu_destroy(struct msm_mmu *mmu)
kfree(iommu);
}
static const struct msm_mmu_funcs funcs = {
static struct device *find_context_bank(const char *name)
{
struct device_node *node = of_find_node_by_name(NULL, name);
struct platform_device *pdev, *parent;
if (!node)
return ERR_PTR(-ENODEV);
if (!of_find_property(node, "iommus", NULL))
return ERR_PTR(-ENODEV);
/* Get the parent device */
parent = of_find_device_by_node(node->parent);
/* Populate the sub nodes */
of_platform_populate(parent->dev.of_node, NULL, NULL, &parent->dev);
/* Get the context bank device */
pdev = of_find_device_by_node(node);
return pdev ? &pdev->dev : ERR_PTR(-ENODEV);
}
static const struct msm_mmu_funcs default_funcs = {
.attach = msm_iommu_attach,
.detach = msm_iommu_detach,
.map = msm_iommu_map,
@ -257,6 +255,14 @@ static const struct msm_mmu_funcs funcs = {
.destroy = msm_iommu_destroy,
};
static const struct msm_mmu_funcs user_funcs = {
.attach = msm_iommu_attach_user,
.detach = msm_iommu_detach,
.map = msm_iommu_map,
.unmap = msm_iommu_unmap,
.destroy = msm_iommu_destroy,
};
static const struct msm_mmu_funcs dynamic_funcs = {
.attach = msm_iommu_attach_dynamic,
.detach = msm_iommu_detach_dynamic,
@ -265,8 +271,22 @@ static const struct msm_mmu_funcs dynamic_funcs = {
.destroy = msm_iommu_destroy,
};
struct msm_mmu *_msm_iommu_new(struct device *dev, struct iommu_domain *domain,
const struct msm_mmu_funcs *funcs)
static const struct {
const char *cbname;
const struct msm_mmu_funcs *funcs;
} msm_iommu_domains[] = {
[MSM_IOMMU_DOMAIN_DEFAULT] = {
.cbname = NULL,
.funcs = &default_funcs,
},
[MSM_IOMMU_DOMAIN_USER] = {
.cbname = "gfx3d_user",
.funcs = &user_funcs,
},
};
static struct msm_mmu *iommu_create(struct device *dev,
struct iommu_domain *domain, const struct msm_mmu_funcs *funcs)
{
struct msm_iommu *iommu;
@ -280,9 +300,23 @@ struct msm_mmu *_msm_iommu_new(struct device *dev, struct iommu_domain *domain,
return &iommu->base;
}
struct msm_mmu *msm_iommu_new(struct device *dev, struct iommu_domain *domain)
struct msm_mmu *msm_iommu_new(struct device *parent,
enum msm_iommu_domain_type type, struct iommu_domain *domain)
{
return _msm_iommu_new(dev, domain, &funcs);
struct device *dev = parent;
if (type >= ARRAY_SIZE(msm_iommu_domains) ||
!msm_iommu_domains[type].funcs)
return ERR_PTR(-ENODEV);
if (msm_iommu_domains[type].cbname) {
dev = find_context_bank(msm_iommu_domains[type].cbname);
if (IS_ERR(dev))
return ERR_CAST(dev);
}
return iommu_create(dev, domain, msm_iommu_domains[type].funcs);
}
/*
@ -307,7 +341,7 @@ struct msm_mmu *msm_iommu_new_dynamic(struct msm_mmu *base)
if (!domain)
return ERR_PTR(-ENODEV);
mmu = _msm_iommu_new(base->dev, domain, &dynamic_funcs);
mmu = iommu_create(base->dev, domain, &dynamic_funcs);
if (IS_ERR(mmu)) {
if (domain)

View file

@ -30,6 +30,11 @@ enum msm_mmu_domain_type {
MSM_SMMU_DOMAIN_MAX,
};
enum msm_iommu_domain_type {
MSM_IOMMU_DOMAIN_DEFAULT,
MSM_IOMMU_DOMAIN_USER,
};
struct msm_mmu_funcs {
int (*attach)(struct msm_mmu *mmu, const char **names, int cnt);
void (*detach)(struct msm_mmu *mmu);
@ -52,9 +57,15 @@ static inline void msm_mmu_init(struct msm_mmu *mmu, struct device *dev,
mmu->funcs = funcs;
}
struct msm_mmu *msm_iommu_new(struct device *dev, struct iommu_domain *domain);
/* Create a new SDE mmu device */
struct msm_mmu *msm_smmu_new(struct device *dev,
enum msm_mmu_domain_type domain);
/* Create a new legacy MDP4 or GPU mmu device */
struct msm_mmu *msm_iommu_new(struct device *parent,
enum msm_iommu_domain_type type, struct iommu_domain *domain);
/* Create a new dynamic domain for GPU */
struct msm_mmu *msm_iommu_new_dynamic(struct msm_mmu *orig);
#endif /* __MSM_MMU_H__ */