On 2015/10/20 23:02, Robin Murphy wrote:
> On 20/10/15 09:45, Chen Feng wrote:
>> iommu/hisilicon: Add hi6220-SoC smmu driver
> 
> A brief description of the smmu and what capabilities it provides wouldn't go 
> amiss here.
> 
ok
>> Signed-off-by: Chen Feng <puck.c...@hisilicon.com>
>> Signed-off-by: Yu Dongbin <yudong...@hisilicon.com>
>> ---
>>   drivers/iommu/Kconfig        |   8 +
>>   drivers/iommu/Makefile       |   1 +
>>   drivers/iommu/hi6220_iommu.c | 482 
>> +++++++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 491 insertions(+)
>>   create mode 100644 drivers/iommu/hi6220_iommu.c
>>
>> diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
>> index 4664c2a..9b41708 100644
>> --- a/drivers/iommu/Kconfig
>> +++ b/drivers/iommu/Kconfig
>> @@ -343,6 +343,14 @@ config SPAPR_TCE_IOMMU
>>         Enables bits of IOMMU API required by VFIO. The iommu_ops
>>         is not implemented as it is not necessary for VFIO.
>>
>> +config HI6220_IOMMU
>> +    bool "Hi6220 IOMMU Support"
>> +    depends on ARM64
>> +    select IOMMU_API
>> +    select IOMMU_IOVA
>> +    help
>> +      Enable IOMMU Driver for hi6220 SoC.
>> +
>>   # ARM IOMMU support
>>   config ARM_SMMU
>>       bool "ARM Ltd. System MMU (SMMU) Support"
>> diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
>> index c6dcc51..ee4dfef 100644
>> --- a/drivers/iommu/Makefile
>> +++ b/drivers/iommu/Makefile
>> @@ -23,3 +23,4 @@ obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o
>>   obj-$(CONFIG_SHMOBILE_IOMMU) += shmobile-iommu.o
>>   obj-$(CONFIG_SHMOBILE_IPMMU) += shmobile-ipmmu.o
>>   obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o
>> +obj-$(CONFIG_HI6220_IOMMU) += hi6220_iommu.o
> 
> It would be nice to insert this before CONFIG_INTEL_IOMMU to try to maintain 
> some semblance of order.
> 
ok
>> diff --git a/drivers/iommu/hi6220_iommu.c b/drivers/iommu/hi6220_iommu.c
>> new file mode 100644
>> index 0000000..6c5198d
>> --- /dev/null
>> +++ b/drivers/iommu/hi6220_iommu.c
>> @@ -0,0 +1,482 @@
>> +/*
>> + * Hisilicon Hi6220 IOMMU driver
>> + *
>> + * Copyright (c) 2015 Hisilicon Limited.
>> + *
>> + * Author: Chen Feng <puck.c...@hisilicon.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#define pr_fmt(fmt) "IOMMU: " fmt
>> +
>> +#include <linux/clk.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/iommu.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/list.h>
>> +#include <linux/irq.h>
>> +#include <linux/of.h>
>> +#include <linux/of_iommu.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/slab.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/iova.h>
>> +#include <linux/sizes.h>
> 
> Those last 3 spoil the ordering here. Plus, do you really need 
> linux/interrupt.h twice?
> 
Accept,remove this later.
>> +#define SMMU_CTRL_OFFSET             (0x0000)
>> +#define SMMU_ENABLE_OFFSET           (0x0004)
>> +#define SMMU_PTBR_OFFSET             (0x0008)
>> +#define SMMU_START_OFFSET            (0x000C)
>> +#define SMMU_END_OFFSET              (0x0010)
>> +#define SMMU_INTMASK_OFFSET          (0x0014)
>> +#define SMMU_RINTSTS_OFFSET          (0x0018)
>> +#define SMMU_MINTSTS_OFFSET          (0x001C)
>> +#define SMMU_INTCLR_OFFSET           (0x0020)
>> +#define SMMU_STATUS_OFFSET           (0x0024)
>> +#define SMMU_AXIID_OFFSET            (0x0028)
>> +#define SMMU_CNTCTRL_OFFSET          (0x002C)
>> +#define SMMU_TRANSCNT_OFFSET         (0x0030)
>> +#define SMMU_L0TLBHITCNT_OFFSET      (0x0034)
>> +#define SMMU_L1TLBHITCNT_OFFSET      (0x0038)
>> +#define SMMU_WRAPCNT_OFFSET          (0x003C)
>> +#define SMMU_SEC_START_OFFSET        (0x0040)
>> +#define SMMU_SEC_END_OFFSET          (0x0044)
>> +#define SMMU_VERSION_OFFSET          (0x0048)
>> +#define SMMU_IPTSRC_OFFSET           (0x004C)
>> +#define SMMU_IPTPA_OFFSET            (0x0050)
>> +#define SMMU_TRBA_OFFSET             (0x0054)
>> +#define SMMU_BYS_START_OFFSET        (0x0058)
>> +#define SMMU_BYS_END_OFFSET          (0x005C)
>> +#define SMMU_RAM_OFFSET              (0x1000)
>> +#define SMMU_REGS_MAX                (15)
> 
> This is confusing; I count 24 registers listed above, what is 15 the maximum 
> of?
> 
>> +#define SMMU_REGS_SGMT_END           (0x60)
>> +#define SMMU_CHIP_ID_V100            (1)
>> +#define SMMU_CHIP_ID_V200            (2)
>> +
>> +#define SMMU_REGS_OPS_SEGMT_START    (0xf00)
>> +#define SMMU_REGS_OPS_SEGMT_NUMB     (8)
>> +#define SMMU_REGS_AXI_SEGMT_START    (0xf80)
>> +#define SMMU_REGS_AXI_SEGMT_NUMB     (8)
>> +
>> +#define SMMU_INIT                    (0x1)
>> +#define SMMU_RUNNING                 (0x2)
>> +#define SMMU_SUSPEND                 (0x3)
>> +#define SMMU_STOP                    (0x4)
>> +#define SMMU_CTRL_INVALID            (BIT(10))
>> +#define PAGE_ENTRY_VALID             (0x1)
>> +
>> +#define IOVA_START_PFN               (1)
>> +#define IOPAGE_SHIFT                 (12)
>> +#define IOVA_PFN(addr)               ((addr) >> IOPAGE_SHIFT)
>> +#define IOVA_PAGE_SZ                 (SZ_4K)
>> +#define IOVA_START                   (0x00002000)
> 
> This doesn't match up - surely either IOVA_START_PFN should be 2, or 
> IOVA_START should be 0x1000.
> 

my fault.
>> +#define IOVA_END                     (0x80000000)
>> +
>> +struct hi6220_smmu {
>> +    unsigned int irq;
>> .......................................
>> +static struct hi6220_smmu *smmu_dev_handle;
>> +static unsigned int smmu_regs_value[SMMU_REGS_MAX] = {0};
>> +static struct iova_domain iova_allocator;
> 
> Why can't these be allocated dynamically as part of the appropriate 
> per-smmu-device data structure? You might only have a single smmu device in 
> this particular SoC, but who knows what the future will bring.
> 

The smmu device who share the pagetable use the iova_allocator here to allocate 
iova address to get different io address.

And the devices can also use their own iova_allocator(not define here,in their 
own driver) for isolate iova allocate.

>> +static struct hi6220_domain *to_hi6220_domain(struct iommu_domain *dom)
>> +{
>> +    return container_of(dom, struct hi6220_domain, io_domain);
>> +}
>> +
>> +static inline void __smmu_writel(struct hi6220_smmu *smmu_dev, u32 value,
>> +                 unsigned long offset)
>> +{
>> +    writel(value, smmu_dev->reg_base + offset);
>> +}
>> +
>> +static inline u32 __smmu_readl(struct hi6220_smmu *smmu_dev,
>> +                   unsigned long offset)
>> +{
>> +    return readl(smmu_dev->reg_base + offset);
>> +}
>> +
>> +static void __restore_regs(struct hi6220_smmu *smmu_dev)
>> +{
>> +    smmu_regs_value[0] = __smmu_readl(smmu_dev, SMMU_CTRL_OFFSET);
>> +    smmu_regs_value[1] = __smmu_readl(smmu_dev, SMMU_ENABLE_OFFSET);
>> +    smmu_regs_value[2] = __smmu_readl(smmu_dev, SMMU_PTBR_OFFSET);
>> +    smmu_regs_value[3] = __smmu_readl(smmu_dev, SMMU_START_OFFSET);
>> +    smmu_regs_value[4] = __smmu_readl(smmu_dev, SMMU_END_OFFSET);
>> +    smmu_regs_value[5] = __smmu_readl(smmu_dev, SMMU_STATUS_OFFSET);
>> +    smmu_regs_value[6] = __smmu_readl(smmu_dev, SMMU_AXIID_OFFSET);
>> +    smmu_regs_value[7] = __smmu_readl(smmu_dev, SMMU_SEC_START_OFFSET);
>> +    smmu_regs_value[8] = __smmu_readl(smmu_dev, SMMU_SEC_END_OFFSET);
>> +    smmu_regs_value[9] = __smmu_readl(smmu_dev, SMMU_VERSION_OFFSET);
>> +    smmu_regs_value[10] = __smmu_readl(smmu_dev, SMMU_IPTSRC_OFFSET);
>> +    smmu_regs_value[11] = __smmu_readl(smmu_dev, SMMU_IPTPA_OFFSET);
>> +    smmu_regs_value[12] = __smmu_readl(smmu_dev, SMMU_TRBA_OFFSET);
>> +    smmu_regs_value[13] = __smmu_readl(smmu_dev, SMMU_BYS_START_OFFSET);
>> +    smmu_regs_value[14] = __smmu_readl(smmu_dev, SMMU_BYS_END_OFFSET);
>> +}
>> +
>> +static void __reload_regs(struct hi6220_smmu *smmu_dev)
>> +{
>> +    __smmu_writel(smmu_dev, smmu_regs_value[2], SMMU_PTBR_OFFSET);
>> +    __smmu_writel(smmu_dev, smmu_regs_value[5], SMMU_STATUS_OFFSET);
>> +    __smmu_writel(smmu_dev, smmu_regs_value[6], SMMU_AXIID_OFFSET);
>> +    __smmu_writel(smmu_dev, smmu_regs_value[7], SMMU_SEC_START_OFFSET);
>> +    __smmu_writel(smmu_dev, smmu_regs_value[8], SMMU_SEC_END_OFFSET);
>> +    __smmu_writel(smmu_dev, smmu_regs_value[9], SMMU_VERSION_OFFSET);
>> +    __smmu_writel(smmu_dev, smmu_regs_value[10], SMMU_IPTSRC_OFFSET);
>> +    __smmu_writel(smmu_dev, smmu_regs_value[11], SMMU_IPTPA_OFFSET);
>> +    __smmu_writel(smmu_dev, smmu_regs_value[12], SMMU_TRBA_OFFSET);
>> +    __smmu_writel(smmu_dev, smmu_regs_value[13], SMMU_BYS_START_OFFSET);
>> +    __smmu_writel(smmu_dev, smmu_regs_value[14], SMMU_BYS_END_OFFSET);
>> +}
> 
> Huh? If you only reload some of the registers you saved, why bother saving 
> all of them in the first place? If that confusing SMMU_REGS_MAX is just to do 
> with how many registers need to be saved/restored, then it would be better 
> named something like SMMU_NUM_VOLATILE_REGS. Or get rid of the confusion 
> entirely and just use a structure to store each one by name, instead of 
> worrying about array sizes and indices.
> 
ok
>> +static inline void __set_smmu_pte(unsigned int *pte,
>> +                  dma_addr_t phys_addr)
>> +{
>> +    if ((*pte & PAGE_ENTRY_VALID))
>> +        pr_err("set pte[%p]->%x already set!\n", pte, *pte);
>> +
>> +    *pte = (unsigned int)(phys_addr | PAGE_ENTRY_VALID);
>> +}
>> +
>> +static inline void __clear_smmu_pte(unsigned int *pte)
>> +{
>> +    if (!(*pte & PAGE_ENTRY_VALID))
>> +        pr_err("clear pte[%p] %x err!\n", pte, *pte);
>> +
>> +    *pte = 0;
>> +}
>> +
>> +static inline void __invalid_smmu_tlb(struct hi6220_domain *m_domain,
>> +                      unsigned long iova, size_t size)
>> +{
>> +    unsigned long flags;
>> +    unsigned int smmu_ctrl = 0;
>> +    unsigned int inv_cnt = 10000;
>> +    unsigned int start_pfn = 0;
>> +    unsigned int end_pfn   = 0;
>> +    struct hi6220_smmu *smmu_dev = m_domain->smmu_dev;
>> +    dma_addr_t smmu_pgtbl_phy = m_domain->smmu_dev->pgtable_phy;
>> +
>> +    spin_lock_irqsave(&smmu_dev_handle->spinlock, flags);
> 
> Why use the global smmu_dev_handle here? You've just nicely looked up your 
> smmu_dev instance through the domain, after all.
> 
accpet,change this later.
>> +
>> +    start_pfn = IOVA_PFN(iova);
>> +    end_pfn   = IOVA_PFN(iova + size);
>> +
>> +    __smmu_writel(smmu_dev, smmu_pgtbl_phy + start_pfn * sizeof(int),
>> +              SMMU_START_OFFSET);
>> +    __smmu_writel(smmu_dev, smmu_pgtbl_phy + end_pfn  * sizeof(int),
>> +              SMMU_END_OFFSET);
>> +
>> +    smmu_ctrl = __smmu_readl(smmu_dev, SMMU_CTRL_OFFSET);
>> +    smmu_ctrl = smmu_ctrl | SMMU_CTRL_INVALID;
>> +    __smmu_writel(smmu_dev, smmu_ctrl, SMMU_CTRL_OFFSET);
>> +
>> +    do {
>> +        smmu_ctrl = __smmu_readl(smmu_dev, SMMU_CTRL_OFFSET);
>> +        if (0x0 == (smmu_ctrl & SMMU_CTRL_INVALID)) {
>> +            spin_unlock_irqrestore(&smmu_dev_handle->spinlock, flags);
>> +            return;
>> +        }
>> +    } while (inv_cnt--);
> 
> Any reason this couldn't be done with readl_poll_timeout_atomic()?
> 
learned,thanks.
>> +    spin_unlock_irqrestore(&smmu_dev_handle->spinlock, flags);
>> +
>> +    WARN_ON((!inv_cnt) && ((smmu_ctrl & SMMU_CTRL_INVALID) != 0));
> 
> As it is, you can only get here if both those conditions were true anyway. I 
> say were, because the post-decrement as you exit the loop means that inv_cnt 
> is now UINT_MAX, thus this WARN will never fire.
> 
ok.
>> +}
>> +
>> +static int __smmu_enable(struct hi6220_smmu *smmu_dev)
>> +{
>> +    if (clk_prepare_enable(smmu_dev->media_sc_clk)) {
>> +        pr_err("clk_prepare_enable media_sc_clk is falied\n");
>> +        return -ENODEV;
>> +    }
>> +    if (clk_prepare_enable(smmu_dev->smmu_peri_clk)) {
>> +        pr_err("clk_prepare_enable smmu_peri_clk is falied\n");
>> +        return -ENODEV;
>> +    }
>> +    if (clk_prepare_enable(smmu_dev->smmu_clk)) {
>> +        pr_err("clk_prepare_enable smmu_clk is falied\n");
>> +        return -ENODEV;
>> +    }
>> +    return 0;
>> +}
>> +
>> +/**
>> + * interrupt happen when operator error
>> + */
>> +static irqreturn_t hi6220_smmu_isr(int irq, void *data)
>> +{
>> +    int          i;
>> +    unsigned int irq_stat;
>> +    struct hi6220_smmu *smmu_dev = smmu_dev_handle;
> 
> Why not just register the appropriate smmu_dev as data in the first place, 
> then you can use it here?
> 
ok
>> +    irq_stat = __smmu_readl(smmu_dev, SMMU_MINTSTS_OFFSET);
>> +
>> +    __smmu_writel(smmu_dev, 0xff, SMMU_INTCLR_OFFSET);
>> +
>> +    for (i = 0; i < SMMU_REGS_SGMT_END; i += 4)
>> +        pr_err("[%08x] ", __smmu_readl(smmu_dev, i));
>> +
>> +    WARN_ON(irq_stat & 0x3f);
> 
> If interrupts are an exceptional condition indicative of a serious error, 
> then this warrants a much clearer message than just dumping some inscrutable 
> hex digits and a useless stack trace to the log with no explanation.
> 
ok,remvoe this warn later.
>> +
>> +    return IRQ_HANDLED;
>> +}
>> +
>> +static bool hi6220_smmu_capable(enum iommu_cap cap)
>> +{
>> +    return false;
>> +}
> 
> You may as well not have this at all, since iommu_capable() will return false 
> for you if the .capable callback isn't implemented.
> 
learned, thanks.
>> +static struct iommu_domain *hi6220_domain_alloc(unsigned type)
>> +{
>> +    struct hi6220_domain *m_domain;
>> +    struct hi6220_smmu *smmu_dev;
>> +
>> +    if (type != IOMMU_DOMAIN_UNMANAGED)
>> +        return NULL;
>> +
>> +    smmu_dev = smmu_dev_handle;
>> +    if (!smmu_dev)
>> +        return NULL;
>> +
>> +    m_domain = kzalloc(sizeof(*m_domain), GFP_KERNEL);
>> +    if (!m_domain)
>> +        return NULL;
>> +
>> +    m_domain->smmu_dev = smmu_dev_handle;
>> +    m_domain->io_domain.geometry.aperture_start = IOVA_START;
>> +    m_domain->io_domain.geometry.aperture_end = IOVA_END;
>> +    m_domain->io_domain.geometry.force_aperture = true;
>> +
>> +    return &m_domain->io_domain;
>> +}
>> +
>> +static void hi6220_domain_free(struct iommu_domain *domain)
>> +{
>> +    struct hi6220_domain *hi6220_domain = to_hi6220_domain(domain);
>> +
>> +    kfree(hi6220_domain);
>> +}
>> +
>> +static int hi6220_smmu_attach_dev(struct iommu_domain *domain,
>> +                  struct device *dev)
>> +{
>> +    dev->archdata.iommu = &iova_allocator;
> 
> If the hardware doesn't offer any kind of device isolation (i.e. multiple 
> translation contexts), then you should probably have more logic here to 
> ensure only a single domain can ever be active at once.
> 

I don't distinguish device by domain. Different domain just use the same 
allocator for sharing page-table.

May be my opinion is wrong. The device also can be in a single domain, but I 
don't know how to ensure the device

get the not-repeat iova address.
>> +    return 0;
>> +}
>> +
>> +static void hi6220_smmu_detach_dev(struct iommu_domain *domain,
>> +                   struct device *dev)
>> +{
>> +    dev->archdata.iommu = NULL;
>> +}
>> +
>> +static inline void dump_pte(unsigned int *pte)
>> +{
>> +    int index;
>> +
>> +    for (index = 0; index < SZ_2M / sizeof(int); index++) {
>> +        if (pte[index])
>> +            pr_info("pte [%p]\t%x\n", &pte[index], pte[index]);
> 
> This should be at most a pr_debug(), if not removed entirely.
> 
ok
>> +    }
>> +}
>> +
>> +static int hi6220_smmu_map(struct iommu_domain *domain,
>> +               unsigned long iova, phys_addr_t pa,
>> +               size_t size, int smmu_prot)
>> +{
>> +    struct hi6220_domain *m_domain = to_hi6220_domain(domain);
>> +    size_t page_size = m_domain->smmu_dev->page_size;
>> +    struct hi6220_smmu *smmu_dev = m_domain->smmu_dev;
>> +    unsigned int *page_table = (unsigned int *)smmu_dev->pgtable_virt;
>> +
>> +    if (size != page_size) {
>> +        pr_err("map size error, only support %zd\n", page_size);
>> +        return -ENOMEM;
>> +    }
> 
> You only advertise one page size, so the IOMMU API will never call you with 
> anything else, therefore this check is completely redundant.
> 
ok.
>> +    __set_smmu_pte(page_table + IOVA_PFN(iova), pa);
>> +
>> +    dump_pte(page_table);
> 
> No.
> 
>> +    __invalid_smmu_tlb(m_domain, iova, size);
>> +
>> +    return 0;
>> +}
>> +
>> +static size_t hi6220_smmu_unmap(struct iommu_domain *domain, unsigned long 
>> iova,
>> +                size_t size)
>> +{
>> +    struct hi6220_domain *m_domain = to_hi6220_domain(domain);
>> +    size_t page_size = m_domain->smmu_dev->page_size;
>> +    struct hi6220_smmu *smmu_dev = m_domain->smmu_dev;
>> +    int *page_table = (unsigned int *)smmu_dev->pgtable_virt;
>> +
>> +    if (size != page_size) {
>> +        pr_err("unmap size error, only support %zd\n", page_size);
>> +        return 0;
>> +    }
> 
> Again, this is just dead code.
> 
ok
>> +
>> +    __clear_smmu_pte(page_table + IOVA_PFN(iova));
>> +
>> +    dump_pte(page_table);
> 
> No.
> 
> Nobody wants to see the *entire* page table dumped for every page 
> mapped/unmapped. Besides, I've found that just turning on the debug prints in 
> iommu.c is usually enough to absolutely cripple performance and overflow the 
> log buffer, so how it's bearable to have this here at all I can't imagine.
> 
>> +    __invalid_smmu_tlb(m_domain, iova, size);
>> +
>> +    return page_size;
>> +}
>> +
>> +static const struct iommu_ops hi6220_smmu_ops = {
>> +    .capable = hi6220_smmu_capable,
>> +    .domain_alloc = hi6220_domain_alloc,
>> +    .domain_free = hi6220_domain_free,
>> +    .attach_dev = hi6220_smmu_attach_dev,
>> +    .detach_dev = hi6220_smmu_detach_dev,
>> +    .map = hi6220_smmu_map,
>> +    .unmap = hi6220_smmu_unmap,
>> +    .map_sg = default_iommu_map_sg,
>> +    .pgsize_bitmap = IOVA_PAGE_SZ,
>> +};
> 
> Since you've implemented neither add_device nor of_xlate, I don't see how any 
> of this ever actually gets used. Are the client devices only using carved-out 
> memory regions outside the kernel linear map and doing their own IOVA 
> management and IOMMU API calls?
>
The device use the iommu_attach_device to get the iova_allocator in 
archdata.iommu, then use this for iova allocate.

>> +static int hi6220_smmu_probe(struct platform_device *pdev)
>> +{
>> +    int ret;
>> +    int irq;
>> +    struct hi6220_smmu *smmu_dev  = NULL;
>> +    struct resource *res = NULL;
>> +
>> +    smmu_dev = devm_kzalloc(&pdev->dev, sizeof(*smmu_dev), GFP_KERNEL);
>> +
>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +    smmu_dev->reg_base = devm_ioremap_resource(&pdev->dev, res);
>> +    if (IS_ERR(smmu_dev->reg_base))
>> +        return PTR_ERR(smmu_dev->reg_base);
>> +
>> +    smmu_dev->media_sc_clk = devm_clk_get(&pdev->dev, "media_sc_clk");
>> +    smmu_dev->smmu_peri_clk  = devm_clk_get(&pdev->dev, "smmu_peri_clk");
>> +    smmu_dev->smmu_clk  = devm_clk_get(&pdev->dev, "smmu_clk");
>> +    if (IS_ERR(smmu_dev->media_sc_clk) || IS_ERR(smmu_dev->smmu_peri_clk) ||
>> +        IS_ERR(smmu_dev->media_sc_clk)) {
>> +        pr_err("clk is not ready!\n");
> 
> ...yet it's safe to carry on probing anyway?
> 
ok, fix this later.
>> +    }
>> +
>> +    irq = platform_get_irq(pdev, 0);
>> +
>> +    ret = devm_request_irq(&pdev->dev, irq, hi6220_smmu_isr, 0,
>> +                   dev_name(&pdev->dev), smmu_dev);
> 
> Oh, so you *do* register the smmu_dev as the callback data. In that case 
> there's really no need for hi6220_smmu_isr() to be using that global pointer 
> after all.
> 
ok
>> +    if (ret) {
>> +        pr_err("Unabled to register handler of irq %d\n", irq);
>> +        return ret;
>> +    }
>> +
>> +    smmu_dev->irq = irq;
>> +    smmu_dev->smmu_isr = hi6220_smmu_isr;
> 
> Why is .smmu_isr needed? It's never used anywhere.
> 
>> +    smmu_dev->page_size = IOVA_PAGE_SZ;
> 
> Similarly, what's the point of .page_size? You already have 
> hi6220_smmu_ops.pgsize_bitmap, and even if the places which refer to 
> page_size were not largely redundant, they could just as well use the 
> constant directly.
> 
>> +    spin_lock_init(&smmu_dev->spinlock);
>> +
>> +    __smmu_enable(smmu_dev);
>> +
>> +    iommu_iova_cache_init();
> 
> This function doesn't exist since 4.3-rc4.
> 
>> +    init_iova_domain(&iova_allocator, IOVA_PAGE_SZ,
>> +             IOVA_START_PFN, IOVA_PFN(DMA_BIT_MASK(32)));
> 
> Why DMA_BIT_MASK(32) and not IOVA_END?
> 
ok
>> +
>> +    dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
>> +    smmu_dev->pgtable_virt = dma_alloc_coherent(&pdev->dev, SZ_2M,
>> +                            &smmu_dev->pgtable_phy,
>> +                            GFP_KERNEL);
>> +    memset(smmu_dev->pgtable_virt, 0, SZ_2M);
> 
> Just use dma_zalloc_coherent(). Also, 2MB is pretty big for a single 
> contiguous allocation, so you should be prepared to handle failure here.
> 
>> +    platform_set_drvdata(pdev, smmu_dev);
>> +    bus_set_iommu(&platform_bus_type, &hi6220_smmu_ops);
>> +    smmu_dev_handle = smmu_dev;
>> +
>> +    return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM
>> +static int hi6220_smmu_suspend(struct platform_device *pdev,
>> +                   pm_message_t state)
>> +{
>> +    struct hi6220_smmu *smmu_dev = dev_get_drvdata(&pdev->dev);
>> +
>> +    __restore_regs(smmu_dev);
>> +
>> +    if (smmu_dev->smmu_clk)
>> +        clk_disable_unprepare(smmu_dev->smmu_clk);
>> +    if (smmu_dev->media_sc_clk)
>> +        clk_disable_unprepare(smmu_dev->media_sc_clk);
>> +    if (smmu_dev->smmu_peri_clk)
>> +        clk_disable_unprepare(smmu_dev->smmu_peri_clk);
>> +
>> +    return 0;
>> +}
>> +
>> +static int hi6220_smmu_resume(struct platform_device *pdev)
>> +{
>> +    struct hi6220_smmu *smmu_dev = dev_get_drvdata(&pdev->dev);
>> +
>> +    __smmu_enable(smmu_dev);
>> +    __reload_regs(smmu_dev);
>> +    return 0;
>> +}
>> +
>> +#else
>> +
>> +#define hi6220_smmu_suspend NULL
>> +#define hi6220_smmu_resume NULL
> 
> Are these necessary? They're not externally visible, and the only references 
> in here are also covered by #ifdef CONFIG_PM.
> 
learned.
>> +#endif /* CONFIG_PM */
>> +
>> +static const struct of_device_id of_smmu_match_tbl[] = {
>> +    {
>> +        .compatible = "hisilicon,hi6220-smmu",
>> +    },
>> +    { }
>> +};
>> +
>> +static struct platform_driver hi6220_smmu_driver = {
>> +    .driver  = {
>> +        .name = "smmu-hi6220",
>> +        .of_match_table = of_smmu_match_tbl,
>> +    },
>> +    .probe  =  hi6220_smmu_probe,
>> +#ifdef CONFIG_PM
>> +    .suspend = hi6220_smmu_suspend,
>> +    .resume  = hi6220_smmu_resume,
>> +#endif
>> +};
>> +
>> +static int __init hi6220_smmu_init(void)
>> +{
>> +    int ret;
>> +
>> +    ret = platform_driver_register(&hi6220_smmu_driver);
>> +    return ret;
>> +}
>> +
>> +subsys_initcall(hi6220_smmu_init);
> 
> The binding in patch 1/3 says #iommu-cells must be 1, which implies you 
> expect client devices to have some kind of ID via the generic IOMMU bindings, 
> but I've now got to the end of the patch without seeing any code for handling 
> master IDs, or indeed anything to even find the client devices in the first 
> place (since you don't implement of_xlate to let the of_iommu code handle 
> that for you). Either this driver isn't finished, or you've got some weird 
> usage model which needs explaining.
> 
        ............
        struct iommu_domain *domain = iommu_domain_alloc(m_dev->bus);
        iommu_attach_device(domain, m_dev);
        struct iova_domain *iovad = (struct iova_domain *)m_dev->archdata.iommu;
        struct iova * t_iova = alloc_iova(iovad, size, max, aligned);
        iommu_map(domain, t_iova->pfn_lo << 12, paddr, size, 0);
        ............
> Robin.
> 
> 
> .
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to