[PATCH 2/7] ARM: Samsung: update/rewrite Samsung SYSMMU (IOMMU) driver

Marek Szyprowski m.szyprowski at samsung.com
Tue Apr 19 04:23:36 EDT 2011


Hello,

On Monday, April 18, 2011 4:13 PM Arnd Bergmann wrote:

> On Monday 18 April 2011, Marek Szyprowski wrote:
> > From: Andrzej Pietrasiewicz <andrzej.p at samsung.com>
> >
> > This patch performs a complete rewrite of sysmmu driver for Samsung
> platform:
> > - simplified the resource management: no more single platform
> >   device with 32 resources is needed, better fits into linux driver model,
> >   each sysmmu instance has it's own resource definition
> > - the new version uses kernel wide common iommu api defined in
> include/iommu.h
> > - cleaned support for sysmmu clocks
> > - added support for custom fault handlers and tlb replacement policy
> 
> Looks like good progress, but I fear that there is still quite a bit more
> work needed here.

Thanks for your comments! I've snipped the minor implementation comments
and focused only on the core iommu API.

(snipped)

> > +struct device *s5p_sysmmu_get(enum s5p_sysmmu_ip ip)
> > +{
> > +       struct device *ret = NULL;
> > +       unsigned long flags;
> > +
> > +       spin_lock_irqsave(&sysmmu_slock, flags);
> > +       if (sysmmu_table[ip]) {
> > +               try_module_get(THIS_MODULE);
> > +               ret = sysmmu_table[ip]->dev;
> > +       }
> > +       spin_unlock_irqrestore(&sysmmu_slock, flags);
> > +
> > +       return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(s5p_sysmmu_get);
> > +
> > +void s5p_sysmmu_put(void *dev)
> > +{
> > +       BUG_ON(!dev);
> > +       module_put(THIS_MODULE);
> > +}
> > +EXPORT_SYMBOL_GPL(s5p_sysmmu_put);
> 
> These look wrong for a number of reasons:
> 
> * try_module_get(THIS_MODULE) makes no sense at all, the idea of the
>   try_module_get is to pin down another module that was calling down,
>   which I suppose is not needed here.
> 
> * This extends the generic IOMMU API in platform specific ways, don't
>   do that.
> 
> * I think you can do without these functions by including a pointer
>   to the iommu structure in dev_archdata, see
>   arch/powerpc/include/asm/device.h for an example.

We heavily based our solution on the iommu implementation found in 
arch/arm/mach-msm/{devices-iommu,iommu,iommu_dev}.c

The s5p_sysmmu_get/put functions are equivalent for msm_iommu_{get,put}_ctx.

(snipped)

> > +static int
> > +s5p_sysmmu_suspend(struct platform_device *pdev, pm_message_t state)
> > +{
> > +       int ret = 0;
> > +       sysmmu_debug(3, "begin\n");
> > +
> > +       return ret;
> > +}
> > +
> > +static int s5p_sysmmu_resume(struct platform_device *pdev)
> > +{
> > +       int ret = 0;
> > +       sysmmu_debug(3, "begin\n");
> > +
> > +       return ret;
> > +}
> > +
> > +static int s5p_sysmmu_runtime_suspend(struct device *dev)
> > +{
> > +       sysmmu_debug(3, "begin\n");
> > +       return 0;
> > +}
> > +
> > +static int s5p_sysmmu_runtime_resume(struct device *dev)
> > +{
> > +       sysmmu_debug(3, "begin\n");
> > +       return 0;
> > +}
> 
> Why even provide these when they don't do anything?

Because they are required by pm_runtime. If no runtime_{suspend,resume}
methods are provided, the pm_runtime core will not call proper methods
on parent device for pmruntime_{get,put}_sync(). The parent device for
each sysmmu platform device is the power domain the sysmmu belongs to.

I know this is crazy, but this is the only way it can be handled now
with runtime_pm.

> > +static int __init
> > +s5p_sysmmu_register(void)
> > +{
> > +       int ret;
> > +
> > +       sysmmu_debug(3, "Registering sysmmu driver...\n");
> > +
> > +       slpt_cache = kmem_cache_create("slpt_cache", 1024, 1024,
> > +                                      SLAB_HWCACHE_ALIGN, NULL);
> > +       if (!slpt_cache) {
> > +               printk(KERN_ERR
> > +                       "%s: failed to allocated slpt cache\n",
> __func__);
> > +               return -ENOMEM;
> > +       }
> > +
> > +       ret = platform_driver_register(&s5p_sysmmu_driver);
> > +
> > +       if (ret) {
> > +               printk(KERN_ERR
> > +                       "%s: failed to register sysmmu driver\n",
> __func__);
> > +               return -EINVAL;
> > +       }
> > +
> > +       register_iommu(&s5p_sysmmu_ops);
> > +
> > +       return ret;
> > +}
> 
> When you register the iommu unconditionally, it becomes impossible for
> this driver to coexist with other iommu drivers in the same kernel,
> which does against the concept of having a platform driver for this.

> It might be better to call the s5p_sysmmu_register function from
> the board files and have no platform devices at all if each IOMMU
> is always bound to a specific device anyway.

Ok, it looks I don't fully get how this iommu.h should be used. It looks
that there can be only one instance of iommu ops registered in the system,
so only one iommu driver can be activated. You are right that the iommu
driver has to be registered on first probe().

I think it might be beneficial to describe a bit more our hardware 
(Exynos4 platform). There are a number of multimedia blocks. Each has it's
own IOMMU controller. Each IOMMU controller has his own set of hardware
registers and irq. There is also a GPU unit (Mali) which has it's own
IOMMU hardware, incompatible with the SYSMMU, so right now it is ignored.

The multimedia blocks are modeled as platform devices and are independent
of platform type (same multimedia blocks can be found on other Samsung
machines, like for example s5pv210/s5pc110), see arch/arm/plat-s5p/dev-*.c
and arch/arm/plat-samsung/dev-*.c.

Platform driver data defined in the above files are registered by each
board startup code, usually by platform_add_devices(), for more details
please check arch/arm/mach-s5pv210/mach-goni.c. There is
struct platform_device *goni_devices[] array which get registered in the
last line in goni_machine_init() function.

For IOMMU controllers on Exynos4 we created an array of platform devices:
extern struct platform_device exynos4_device_sysmmu[];

Now the board startup code registers only these sysmmu controllers
(instances) that are required on the particular board. See "[PATCH 7/7]
ARM: EXYNOS4: enable FIMC on Universal_C210":
@@ -613,6 +616,15 @@ static struct platform_device *universal_devices[]
__initdata = {
        &s3c_device_hsmmc2,
        &s3c_device_hsmmc3,
        &s3c_device_i2c5,
+       &s5p_device_fimc0,
+       &s5p_device_fimc1,
+       &s5p_device_fimc2,
+       &s5p_device_fimc3,
+       &exynos4_device_pd[PD_CAM],
+       &exynos4_device_sysmmu[S5P_SYSMMU_FIMC0],
+       &exynos4_device_sysmmu[S5P_SYSMMU_FIMC1],
+       &exynos4_device_sysmmu[S5P_SYSMMU_FIMC2],
+       &exynos4_device_sysmmu[S5P_SYSMMU_FIMC3],

We need to map the above structure into linux/iommu.h api.

The domain defined in iommu api are quite straightforward. Each domain 
is just a set of mappings between physical addresses (phys) and io addresses
(iova).

For the drivers the most important are the following functions:
iommu_{attach,detach}_device(struct iommu_domain *domain, struct device *dev);

We assumed that they just assign the domain (mapping) to particular instance
of iommu. However the driver need to get somehow the pointer to the iommu 
instance. That's why we added the s5p_sysmmu_{get,put} functions. 

Now I see that you want to make the clients (drivers) to provide their own
struct device pointer to the iommu_{attach,detach}_device() function instead of
giving there a pointer to iommu device. Am I right? We will need some kind of
mapping between multimedia devices and particular instanced of sysmmu
controllers.

There will be also some problems with such approach. Mainly we have a
multimedia codec module, which have 2 memory controllers (for faster transfers)
and 2 iommu controllers. How can we handle such case?

> > diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-
> samsung/include/plat/devs.h
> > index f0da6b7..0ae5dd0 100644
> > --- a/arch/arm/plat-samsung/include/plat/devs.h
> > +++ b/arch/arm/plat-samsung/include/plat/devs.h
> > @@ -142,7 +142,7 @@ extern struct platform_device s5p_device_fimc3;
> >  extern struct platform_device s5p_device_mipi_csis0;
> >  extern struct platform_device s5p_device_mipi_csis1;
> >
> > -extern struct platform_device exynos4_device_sysmmu;
> > +extern struct platform_device exynos4_device_sysmmu[];
> 
> Why is this a global variable? I would expect this to be private to the
> implementation.

To allow each board to register only particular instances of sysmmu controllers.

Best regards
-- 
Marek Szyprowski
Samsung Poland R&D Center



More information about the linux-arm-kernel mailing list