Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] mm: pmd dirty emulation in page fault handler
From: Michal Hocko @ 2016-12-23  9:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161222145203.GA18970@bbox>

On Thu 22-12-16 23:52:03, Minchan Kim wrote:
[...]
> >From b3ec95c0df91ad113525968a4a6b53030fd0b48d Mon Sep 17 00:00:00 2001
> From: Minchan Kim <minchan@kernel.org>
> Date: Thu, 22 Dec 2016 23:43:49 +0900
> Subject: [PATCH v2] mm: pmd dirty emulation in page fault handler
> 
> Andreas reported [1] made a test in jemalloc hang in THP mode in arm64.
> http://lkml.kernel.org/r/mvmmvfy37g1.fsf at hawking.suse.de
> 
> The problem is page fault handler supports only accessed flag emulation
> for THP page of SW-dirty/accessed architecture.
> 
> This patch enables dirty-bit emulation for those architectures.
> Without it, MADV_FREE makes application hang by repeated fault forever.

The changelog is rather terse and considering the issue is rather subtle
and it aims the stable tree I think it could see more information. How
do we end up looping in the page fault and why the dirty pmd stops it.
Could you update the changelog to be more verbose, please? I am still
digesting this patch but I believe it is correct fwiw...

Thanks!

> [1] b8d3c4c3009d, mm/huge_memory.c: don't split THP page when MADV_FREE syscall is called
> 
> Cc: Jason Evans <je@fb.com>
> Cc: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
> Cc: Will Deacon <will.deacon@arm.com>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: linux-arch at vger.kernel.org
> Cc: linux-arm-kernel at lists.infradead.org
> Cc: <stable@vger.kernel.org> [4.5+]
> Fixes: b8d3c4c3009d ("mm/huge_memory.c: don't split THP page when MADV_FREE syscall is called")
> Reported-by: Andreas Schwab <schwab@suse.de>
> Signed-off-by: Minchan Kim <minchan@kernel.org>
> ---
> * from v1
>   * Remove __handle_mm_fault part - Kirill
> 
>  mm/huge_memory.c | 6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> index 10eedbf..29ec8a4 100644
> --- a/mm/huge_memory.c
> +++ b/mm/huge_memory.c
> @@ -883,15 +883,17 @@ void huge_pmd_set_accessed(struct vm_fault *vmf, pmd_t orig_pmd)
>  {
>  	pmd_t entry;
>  	unsigned long haddr;
> +	bool write = vmf->flags & FAULT_FLAG_WRITE;
>  
>  	vmf->ptl = pmd_lock(vmf->vma->vm_mm, vmf->pmd);
>  	if (unlikely(!pmd_same(*vmf->pmd, orig_pmd)))
>  		goto unlock;
>  
>  	entry = pmd_mkyoung(orig_pmd);
> +	if (write)
> +		entry = pmd_mkdirty(entry);
>  	haddr = vmf->address & HPAGE_PMD_MASK;
> -	if (pmdp_set_access_flags(vmf->vma, haddr, vmf->pmd, entry,
> -				vmf->flags & FAULT_FLAG_WRITE))
> +	if (pmdp_set_access_flags(vmf->vma, haddr, vmf->pmd, entry, write))
>  		update_mmu_cache_pmd(vmf->vma, vmf->address, vmf->pmd);
>  
>  unlock:
> -- 
> 2.7.4
> 
> --
> To unsubscribe, send a message with 'unsubscribe linux-mm' in
> the body to majordomo at kvack.org.  For more info on Linux MM,
> see: http://www.linux-mm.org/ .
> Don't email: <a href=mailto:"dont@kvack.org"> email at kvack.org </a>

-- 
Michal Hocko
SUSE Labs

^ permalink raw reply

* [PATCHv2 0/3] pci: remove msi_controller registration API
From: Thomas Petazzoni @ 2016-12-23  9:20 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

This small patch series aims at removing the small msi_controller
registration API implemented in drivers/of/of_pci.c.

In order to do so, it moves the last two users of this API to the
generic MSI support:

 - The pci-aardvark driver is moved to use the generic MSI
   support. This driver is both the provider of the MSI capability and
   the consumer of this capability.

 - The pci-mvebu driver is cleaned up from using the msi_controller
   registration API. This driver is only consuming MSI capabilies from
   another driver (irq-armada-370-xp) which was migrated to the
   generic MSI support some time ago. Therefore, this patch is simply
   removing dead code.

Finally, the last commit removed the now unused API.

Changes between v1 to v2 are described in each individual patch.

Thanks,

Thomas

Thomas Petazzoni (3):
  pci: pci-aardvark: move to MSI handling using generic MSI support
  pci: pci-mvebu: remove useless MSI enabling code
  of: pci: remove unused MSI controller helpers

 drivers/of/of_pci.c             |  45 -----------
 drivers/pci/host/pci-aardvark.c | 172 +++++++++++++++-------------------------
 drivers/pci/host/pci-mvebu.c    |  17 ----
 include/linux/of_pci.h          |  11 ---
 4 files changed, 66 insertions(+), 179 deletions(-)

-- 
2.7.4

^ permalink raw reply

* [PATCHv2 1/3] pci: pci-aardvark: move to MSI handling using generic MSI support
From: Thomas Petazzoni @ 2016-12-23  9:20 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1482484854-26418-1-git-send-email-thomas.petazzoni@free-electrons.com>

The MSI support introduced with the initial Aardvark driver was based
on the msi_controller structure and the of_pci_msi_chip_add() /
of_pci_find_msi_chip_by_node() API, which are being deprecated in
favor of the generic MSI support.

Therefore, this commit updates the Aardvark driver to use the generic
MSI support.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
Changes since v1:
 - Rebased on top of v4.9
 - Removed the MSI_FLAG_PCI_MSIX flag since we don't support MSI-X for
   the moment.
---
 drivers/pci/host/pci-aardvark.c | 172 +++++++++++++++-------------------------
 1 file changed, 66 insertions(+), 106 deletions(-)

diff --git a/drivers/pci/host/pci-aardvark.c b/drivers/pci/host/pci-aardvark.c
index 4fce494..3fdca5d 100644
--- a/drivers/pci/host/pci-aardvark.c
+++ b/drivers/pci/host/pci-aardvark.c
@@ -200,10 +200,12 @@ struct advk_pcie {
 	struct list_head resources;
 	struct irq_domain *irq_domain;
 	struct irq_chip irq_chip;
-	struct msi_controller msi;
 	struct irq_domain *msi_domain;
+	struct irq_domain *msi_inner_domain;
+	struct irq_chip msi_bottom_irq_chip;
 	struct irq_chip msi_irq_chip;
-	DECLARE_BITMAP(msi_irq_in_use, MSI_IRQ_NUM);
+	struct msi_domain_info msi_domain_info;
+	DECLARE_BITMAP(msi_used, MSI_IRQ_NUM);
 	struct mutex msi_used_lock;
 	u16 msi_msg;
 	int root_bus_nr;
@@ -545,94 +547,64 @@ static struct pci_ops advk_pcie_ops = {
 	.write = advk_pcie_wr_conf,
 };
 
-static int advk_pcie_alloc_msi(struct advk_pcie *pcie)
+static void advk_msi_irq_compose_msi_msg(struct irq_data *data,
+					 struct msi_msg *msg)
 {
-	int hwirq;
+	struct advk_pcie *pcie = irq_data_get_irq_chip_data(data);
+	phys_addr_t msi_msg = virt_to_phys(&pcie->msi_msg);
 
-	mutex_lock(&pcie->msi_used_lock);
-	hwirq = find_first_zero_bit(pcie->msi_irq_in_use, MSI_IRQ_NUM);
-	if (hwirq >= MSI_IRQ_NUM)
-		hwirq = -ENOSPC;
-	else
-		set_bit(hwirq, pcie->msi_irq_in_use);
-	mutex_unlock(&pcie->msi_used_lock);
-
-	return hwirq;
+	msg->address_lo = lower_32_bits(msi_msg);
+	msg->address_hi = upper_32_bits(msi_msg);
+	msg->data = data->irq;
 }
 
-static void advk_pcie_free_msi(struct advk_pcie *pcie, int hwirq)
+static int advk_msi_set_affinity(struct irq_data *irq_data,
+				 const struct cpumask *mask, bool force)
 {
-	struct device *dev = &pcie->pdev->dev;
-
-	mutex_lock(&pcie->msi_used_lock);
-	if (!test_bit(hwirq, pcie->msi_irq_in_use))
-		dev_err(dev, "trying to free unused MSI#%d\n", hwirq);
-	else
-		clear_bit(hwirq, pcie->msi_irq_in_use);
-	mutex_unlock(&pcie->msi_used_lock);
+	return -EINVAL;
 }
 
-static int advk_pcie_setup_msi_irq(struct msi_controller *chip,
-				   struct pci_dev *pdev,
-				   struct msi_desc *desc)
+static int advk_msi_irq_domain_alloc(struct irq_domain *domain,
+				     unsigned int virq,
+				     unsigned int nr_irqs, void *args)
 {
-	struct advk_pcie *pcie = pdev->bus->sysdata;
-	struct msi_msg msg;
-	int virq, hwirq;
-	phys_addr_t msi_msg_phys;
-
-	/* We support MSI, but not MSI-X */
-	if (desc->msi_attrib.is_msix)
-		return -EINVAL;
-
-	hwirq = advk_pcie_alloc_msi(pcie);
-	if (hwirq < 0)
-		return hwirq;
+	struct advk_pcie *pcie = domain->host_data;
+	int hwirq, i;
 
-	virq = irq_create_mapping(pcie->msi_domain, hwirq);
-	if (!virq) {
-		advk_pcie_free_msi(pcie, hwirq);
-		return -EINVAL;
+	mutex_lock(&pcie->msi_used_lock);
+	hwirq = bitmap_find_next_zero_area(pcie->msi_used, MSI_IRQ_NUM,
+					   0, nr_irqs, 0);
+	if (hwirq >= MSI_IRQ_NUM) {
+		mutex_unlock(&pcie->msi_used_lock);
+		return -ENOSPC;
 	}
 
-	irq_set_msi_desc(virq, desc);
-
-	msi_msg_phys = virt_to_phys(&pcie->msi_msg);
-
-	msg.address_lo = lower_32_bits(msi_msg_phys);
-	msg.address_hi = upper_32_bits(msi_msg_phys);
-	msg.data = virq;
-
-	pci_write_msi_msg(virq, &msg);
-
-	return 0;
-}
+	bitmap_set(pcie->msi_used, hwirq, nr_irqs);
+	mutex_unlock(&pcie->msi_used_lock);
 
-static void advk_pcie_teardown_msi_irq(struct msi_controller *chip,
-				       unsigned int irq)
-{
-	struct irq_data *d = irq_get_irq_data(irq);
-	struct msi_desc *msi = irq_data_get_msi_desc(d);
-	struct advk_pcie *pcie = msi_desc_to_pci_sysdata(msi);
-	unsigned long hwirq = d->hwirq;
+	for (i = 0; i < nr_irqs; i++)
+		irq_domain_set_info(domain, virq + i, hwirq + i,
+				    &pcie->msi_bottom_irq_chip,
+				    domain->host_data, handle_simple_irq,
+				    NULL, NULL);
 
-	irq_dispose_mapping(irq);
-	advk_pcie_free_msi(pcie, hwirq);
+	return hwirq;
 }
 
-static int advk_pcie_msi_map(struct irq_domain *domain,
-			     unsigned int virq, irq_hw_number_t hw)
+static void advk_msi_irq_domain_free(struct irq_domain *domain,
+				     unsigned int virq, unsigned int nr_irqs)
 {
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
 	struct advk_pcie *pcie = domain->host_data;
 
-	irq_set_chip_and_handler(virq, &pcie->msi_irq_chip,
-				 handle_simple_irq);
-
-	return 0;
+	mutex_lock(&pcie->msi_used_lock);
+	bitmap_clear(pcie->msi_used, d->hwirq, nr_irqs);
+	mutex_unlock(&pcie->msi_used_lock);
 }
 
-static const struct irq_domain_ops advk_pcie_msi_irq_ops = {
-	.map = advk_pcie_msi_map,
+static const struct irq_domain_ops advk_msi_domain_ops = {
+	.alloc = advk_msi_irq_domain_alloc,
+	.free = advk_msi_irq_domain_free,
 };
 
 static void advk_pcie_irq_mask(struct irq_data *d)
@@ -680,30 +652,24 @@ static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie)
 {
 	struct device *dev = &pcie->pdev->dev;
 	struct device_node *node = dev->of_node;
-	struct irq_chip *msi_irq_chip;
-	struct msi_controller *msi;
+	struct irq_chip *bottom_ic, *msi_ic;
+	struct msi_domain_info *msi_di;
 	phys_addr_t msi_msg_phys;
-	int ret;
 
-	msi_irq_chip = &pcie->msi_irq_chip;
+	mutex_init(&pcie->msi_used_lock);
 
-	msi_irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-msi",
-					    dev_name(dev));
-	if (!msi_irq_chip->name)
-		return -ENOMEM;
+	bottom_ic = &pcie->msi_bottom_irq_chip;
 
-	msi_irq_chip->irq_enable = pci_msi_unmask_irq;
-	msi_irq_chip->irq_disable = pci_msi_mask_irq;
-	msi_irq_chip->irq_mask = pci_msi_mask_irq;
-	msi_irq_chip->irq_unmask = pci_msi_unmask_irq;
+	bottom_ic->name = "MSI";
+	bottom_ic->irq_compose_msi_msg = advk_msi_irq_compose_msi_msg;
+	bottom_ic->irq_set_affinity = advk_msi_set_affinity;
 
-	msi = &pcie->msi;
+	msi_ic = &pcie->msi_irq_chip;
+	msi_ic->name = "advk-MSI";
 
-	msi->setup_irq = advk_pcie_setup_msi_irq;
-	msi->teardown_irq = advk_pcie_teardown_msi_irq;
-	msi->of_node = node;
-
-	mutex_init(&pcie->msi_used_lock);
+	msi_di = &pcie->msi_domain_info;
+	msi_di->flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS;
+	msi_di->chip = msi_ic;
 
 	msi_msg_phys = virt_to_phys(&pcie->msi_msg);
 
@@ -712,16 +678,18 @@ static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie)
 	advk_writel(pcie, upper_32_bits(msi_msg_phys),
 		    PCIE_MSI_ADDR_HIGH_REG);
 
-	pcie->msi_domain =
+	pcie->msi_inner_domain =
 		irq_domain_add_linear(NULL, MSI_IRQ_NUM,
-				      &advk_pcie_msi_irq_ops, pcie);
-	if (!pcie->msi_domain)
+				      &advk_msi_domain_ops, pcie);
+	if (!pcie->msi_inner_domain)
 		return -ENOMEM;
 
-	ret = of_pci_msi_chip_add(msi);
-	if (ret < 0) {
-		irq_domain_remove(pcie->msi_domain);
-		return ret;
+	pcie->msi_domain =
+		pci_msi_create_irq_domain(of_node_to_fwnode(node),
+					  msi_di, pcie->msi_inner_domain);
+	if (!pcie->msi_domain) {
+		irq_domain_remove(pcie->msi_inner_domain);
+		return -ENOMEM;
 	}
 
 	return 0;
@@ -729,8 +697,8 @@ static int advk_pcie_init_msi_irq_domain(struct advk_pcie *pcie)
 
 static void advk_pcie_remove_msi_irq_domain(struct advk_pcie *pcie)
 {
-	of_pci_msi_chip_remove(&pcie->msi);
 	irq_domain_remove(pcie->msi_domain);
+	irq_domain_remove(pcie->msi_inner_domain);
 }
 
 static int advk_pcie_init_irq_domain(struct advk_pcie *pcie)
@@ -917,8 +885,6 @@ static int advk_pcie_probe(struct platform_device *pdev)
 	struct advk_pcie *pcie;
 	struct resource *res;
 	struct pci_bus *bus, *child;
-	struct msi_controller *msi;
-	struct device_node *msi_node;
 	int ret, irq;
 
 	pcie = devm_kzalloc(dev, sizeof(struct advk_pcie), GFP_KERNEL);
@@ -962,14 +928,8 @@ static int advk_pcie_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	msi_node = of_parse_phandle(dev->of_node, "msi-parent", 0);
-	if (msi_node)
-		msi = of_pci_find_msi_chip_by_node(msi_node);
-	else
-		msi = NULL;
-
-	bus = pci_scan_root_bus_msi(dev, 0, &advk_pcie_ops,
-				    pcie, &pcie->resources, &pcie->msi);
+	bus = pci_scan_root_bus(dev, 0, &advk_pcie_ops,
+				pcie, &pcie->resources);
 	if (!bus) {
 		advk_pcie_remove_msi_irq_domain(pcie);
 		advk_pcie_remove_irq_domain(pcie);
-- 
2.7.4

^ permalink raw reply related

* [PATCHv2 2/3] pci: pci-mvebu: remove useless MSI enabling code
From: Thomas Petazzoni @ 2016-12-23  9:20 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1482484854-26418-1-git-send-email-thomas.petazzoni@free-electrons.com>

Since commit fcc392d501bd2 ("irqchip/armada-370-xp: Use the generic MSI
infrastructure"), the irqchip driver used on Armada 370, XP, 375, 38x,
39x for the MPIC interrupt controller has been converted to use the
generic MSI infrastructure.

Since this commit, it is no longer registering an msi_controller
structure with the of_pci_msi_chip_add() function. Therefore, having the
PCI driver used on the same platform calling
of_pci_find_msi_chip_by_node() is pretty useless.

The MSI resolution is now done in the generic interrupt resolution code,
since the MSI controller is an irq domain attached to the interrupt
controller node, which is pointed to by the msi-parent DT property in
the PCIe controller node.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
Changes since v1:
 - Rebased on v4.9.
---
 drivers/pci/host/pci-mvebu.c | 17 -----------------
 1 file changed, 17 deletions(-)

diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c
index 45a89d9..2100dbd 100644
--- a/drivers/pci/host/pci-mvebu.c
+++ b/drivers/pci/host/pci-mvebu.c
@@ -986,22 +986,6 @@ static int mvebu_get_tgt_attr(struct device_node *np, int devfn,
 	return -ENOENT;
 }
 
-static void mvebu_pcie_msi_enable(struct mvebu_pcie *pcie)
-{
-	struct device_node *msi_node;
-
-	msi_node = of_parse_phandle(pcie->pdev->dev.of_node,
-				    "msi-parent", 0);
-	if (!msi_node)
-		return;
-
-	pcie->msi = of_pci_find_msi_chip_by_node(msi_node);
-	of_node_put(msi_node);
-
-	if (pcie->msi)
-		pcie->msi->dev = &pcie->pdev->dev;
-}
-
 #ifdef CONFIG_PM_SLEEP
 static int mvebu_pcie_suspend(struct device *dev)
 {
@@ -1279,7 +1263,6 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
 	for (i = 0; i < (IO_SPACE_LIMIT - SZ_64K); i += SZ_64K)
 		pci_ioremap_io(i, pcie->io.start + i);
 
-	mvebu_pcie_msi_enable(pcie);
 	mvebu_pcie_enable(pcie);
 
 	platform_set_drvdata(pdev, pcie);
-- 
2.7.4

^ permalink raw reply related

* [PATCHv2 3/3] of: pci: remove unused MSI controller helpers
From: Thomas Petazzoni @ 2016-12-23  9:20 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1482484854-26418-1-git-send-email-thomas.petazzoni@free-electrons.com>

All the users of the small MSI controller API have been migrated to use
the generic MSI infrastructure instead. We no longer need a global
chained list of msi_controller. Instead, MSI controllers are now
represented as irq domains attached to OF nodes, and the resolution
between a device requesting an MSI and the corresponding MSI controller
is done by the generic interrupt resolution logic.

Therefore, this API is now completely useless, and can be removed from
the kernel.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Acked-by: Marc Zyngier <marc.zyngier@arm.com>
Acked-by: Rob Herring <robh@kernel.org>
---
Changes since v1:
 - Rebased on v4.9.
 - Added ACKs from Marc Zyngier and Rob Herring, given on the v1 (the
   patch hasn't changed except from resolving the conflicts when
   rebasing on v4.9, and those conflicts were trivial since this patch
   only removes code).
---
 drivers/of/of_pci.c    | 45 ---------------------------------------------
 include/linux/of_pci.h | 11 -----------
 2 files changed, 56 deletions(-)

diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c
index b58be12..3ea9631 100644
--- a/drivers/of/of_pci.c
+++ b/drivers/of/of_pci.c
@@ -264,51 +264,6 @@ int of_pci_get_host_bridge_resources(struct device_node *dev,
 EXPORT_SYMBOL_GPL(of_pci_get_host_bridge_resources);
 #endif /* CONFIG_OF_ADDRESS */
 
-#ifdef CONFIG_PCI_MSI
-
-static LIST_HEAD(of_pci_msi_chip_list);
-static DEFINE_MUTEX(of_pci_msi_chip_mutex);
-
-int of_pci_msi_chip_add(struct msi_controller *chip)
-{
-	if (!of_property_read_bool(chip->of_node, "msi-controller"))
-		return -EINVAL;
-
-	mutex_lock(&of_pci_msi_chip_mutex);
-	list_add(&chip->list, &of_pci_msi_chip_list);
-	mutex_unlock(&of_pci_msi_chip_mutex);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(of_pci_msi_chip_add);
-
-void of_pci_msi_chip_remove(struct msi_controller *chip)
-{
-	mutex_lock(&of_pci_msi_chip_mutex);
-	list_del(&chip->list);
-	mutex_unlock(&of_pci_msi_chip_mutex);
-}
-EXPORT_SYMBOL_GPL(of_pci_msi_chip_remove);
-
-struct msi_controller *of_pci_find_msi_chip_by_node(struct device_node *of_node)
-{
-	struct msi_controller *c;
-
-	mutex_lock(&of_pci_msi_chip_mutex);
-	list_for_each_entry(c, &of_pci_msi_chip_list, list) {
-		if (c->of_node == of_node) {
-			mutex_unlock(&of_pci_msi_chip_mutex);
-			return c;
-		}
-	}
-	mutex_unlock(&of_pci_msi_chip_mutex);
-
-	return NULL;
-}
-EXPORT_SYMBOL_GPL(of_pci_find_msi_chip_by_node);
-
-#endif /* CONFIG_PCI_MSI */
-
 /**
  * of_pci_map_rid - Translate a requester ID through a downstream mapping.
  * @np: root complex device node.
diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h
index 7fd5cfc..77471bf 100644
--- a/include/linux/of_pci.h
+++ b/include/linux/of_pci.h
@@ -78,15 +78,4 @@ static inline int of_pci_get_host_bridge_resources(struct device_node *dev,
 }
 #endif
 
-#if defined(CONFIG_OF) && defined(CONFIG_PCI_MSI)
-int of_pci_msi_chip_add(struct msi_controller *chip);
-void of_pci_msi_chip_remove(struct msi_controller *chip);
-struct msi_controller *of_pci_find_msi_chip_by_node(struct device_node *of_node);
-#else
-static inline int of_pci_msi_chip_add(struct msi_controller *chip) { return -EINVAL; }
-static inline void of_pci_msi_chip_remove(struct msi_controller *chip) { }
-static inline struct msi_controller *
-of_pci_find_msi_chip_by_node(struct device_node *of_node) { return NULL; }
-#endif
-
 #endif
-- 
2.7.4

^ permalink raw reply related

* [PATCH] [media] atmel-isc: add the isc pipeline function
From: Songjun Wu @ 2016-12-23  9:24 UTC (permalink / raw)
  To: linux-arm-kernel

Image Sensor Controller has an internal image processor.
It can convert raw format to the other formats, like
RGB565, YUV420P. A module parameter 'sensor_preferred'
is used to enable or disable the pipeline function.
Some v4l2 controls are added to tuning the image when
the pipeline function is enabled.

Signed-off-by: Songjun Wu <songjun.wu@microchip.com>
---

 drivers/media/platform/atmel/atmel-isc-regs.h | 102 ++++-
 drivers/media/platform/atmel/atmel-isc.c      | 629 +++++++++++++++++++++-----
 2 files changed, 623 insertions(+), 108 deletions(-)

diff --git a/drivers/media/platform/atmel/atmel-isc-regs.h b/drivers/media/platform/atmel/atmel-isc-regs.h
index 00c449717cde..6936ac467609 100644
--- a/drivers/media/platform/atmel/atmel-isc-regs.h
+++ b/drivers/media/platform/atmel/atmel-isc-regs.h
@@ -65,6 +65,7 @@
 #define ISC_INTSR       0x00000034
 
 #define ISC_INT_DDONE		BIT(8)
+#define ISC_INT_HISDONE		BIT(12)
 
 /* ISC White Balance Control Register */
 #define ISC_WB_CTRL     0x00000058
@@ -72,30 +73,98 @@
 /* ISC White Balance Configuration Register */
 #define ISC_WB_CFG      0x0000005c
 
+/* ISC White Balance Offset for R, GR Register */
+#define ISC_WB_O_RGR	0x00000060
+
+/* ISC White Balance Offset for B, GB Register */
+#define ISC_WB_O_BGR	0x00000064
+
+/* ISC White Balance Gain for R, GR Register */
+#define ISC_WB_G_RGR	0x00000068
+
+/* ISC White Balance Gain for B, GB Register */
+#define ISC_WB_G_BGR	0x0000006c
+
 /* ISC Color Filter Array Control Register */
 #define ISC_CFA_CTRL    0x00000070
 
 /* ISC Color Filter Array Configuration Register */
 #define ISC_CFA_CFG     0x00000074
+#define ISC_CFA_CFG_EITPOL	BIT(4)
 
 #define ISC_BAY_CFG_GRGR	0x0
 #define ISC_BAY_CFG_RGRG	0x1
 #define ISC_BAY_CFG_GBGB	0x2
 #define ISC_BAY_CFG_BGBG	0x3
-#define ISC_BAY_CFG_MASK	GENMASK(1, 0)
 
 /* ISC Color Correction Control Register */
 #define ISC_CC_CTRL     0x00000078
 
+/* ISC Color Correction RR RG Register */
+#define ISC_CC_RR_RG	0x0000007c
+
+/* ISC Color Correction RB OR Register */
+#define ISC_CC_RB_OR	0x00000080
+
+/* ISC Color Correction GR GG Register */
+#define ISC_CC_GR_GG	0x00000084
+
+/* ISC Color Correction GB OG Register */
+#define ISC_CC_GB_OG	0x00000088
+
+/* ISC Color Correction BR BG Register */
+#define ISC_CC_BR_BG	0x0000008c
+
+/* ISC Color Correction BB OB Register */
+#define ISC_CC_BB_OB	0x00000090
+
 /* ISC Gamma Correction Control Register */
 #define ISC_GAM_CTRL    0x00000094
 
+/* ISC_Gamma Correction Blue Entry Register */
+#define ISC_GAM_BENTRY	0x00000098
+
+/* ISC_Gamma Correction Green Entry Register */
+#define ISC_GAM_GENTRY	0x00000198
+
+/* ISC_Gamma Correction Green Entry Register */
+#define ISC_GAM_RENTRY	0x00000298
+
 /* Color Space Conversion Control Register */
 #define ISC_CSC_CTRL    0x00000398
 
+/* Color Space Conversion YR YG Register */
+#define ISC_CSC_YR_YG	0x0000039c
+
+/* Color Space Conversion YB OY Register */
+#define ISC_CSC_YB_OY	0x000003a0
+
+/* Color Space Conversion CBR CBG Register */
+#define ISC_CSC_CBR_CBG	0x000003a4
+
+/* Color Space Conversion CBB OCB Register */
+#define ISC_CSC_CBB_OCB	0x000003a8
+
+/* Color Space Conversion CRR CRG Register */
+#define ISC_CSC_CRR_CRG	0x000003ac
+
+/* Color Space Conversion CRB OCR Register */
+#define ISC_CSC_CRB_OCR	0x000003b0
+
 /* Contrast And Brightness Control Register */
 #define ISC_CBC_CTRL    0x000003b4
 
+/* Contrast And Brightness Configuration Register */
+#define ISC_CBC_CFG	0x000003b8
+
+/* Brightness Register */
+#define ISC_CBC_BRIGHT	0x000003bc
+#define ISC_CBC_BRIGHT_MASK	GENMASK(10, 0)
+
+/* Contrast Register */
+#define ISC_CBC_CONTRAST	0x000003c0
+#define ISC_CBC_CONTRAST_MASK	GENMASK(11, 0)
+
 /* Subsampling 4:4:4 to 4:2:2 Control Register */
 #define ISC_SUB422_CTRL 0x000003c4
 
@@ -120,6 +189,27 @@
 #define ISC_RLP_CFG_MODE_YYCC_LIMITED   0xc
 #define ISC_RLP_CFG_MODE_MASK           GENMASK(3, 0)
 
+/* Histogram Control Register */
+#define ISC_HIS_CTRL	0x000003d4
+
+#define ISC_HIS_CTRL_EN			BIT(0)
+#define ISC_HIS_CTRL_DIS		0x0
+
+/* Histogram Configuration Register */
+#define ISC_HIS_CFG	0x000003d8
+
+#define ISC_HIS_CFG_MODE_GR		0x0
+#define ISC_HIS_CFG_MODE_R		0x1
+#define ISC_HIS_CFG_MODE_GB		0x2
+#define ISC_HIS_CFG_MODE_B		0x3
+#define ISC_HIS_CFG_MODE_Y		0x4
+#define ISC_HIS_CFG_MODE_RAW		0x5
+#define ISC_HIS_CFG_MODE_YCCIR656	0x6
+
+#define ISC_HIS_CFG_BAYSEL_SHIFT	4
+
+#define ISC_HIS_CFG_RAR			BIT(8)
+
 /* DMA Configuration Register */
 #define ISC_DCFG        0x000003e0
 #define ISC_DCFG_IMODE_PACKED8          0x0
@@ -159,7 +249,13 @@
 /* DMA Address 0 Register */
 #define ISC_DAD0        0x000003ec
 
-/* DMA Stride 0 Register */
-#define ISC_DST0        0x000003f0
+/* DMA Address 1 Register */
+#define ISC_DAD1        0x000003f4
+
+/* DMA Address 2 Register */
+#define ISC_DAD2        0x000003fc
+
+/* Histogram Entry */
+#define ISC_HIS_ENTRY	0x00000410
 
 #endif
diff --git a/drivers/media/platform/atmel/atmel-isc.c b/drivers/media/platform/atmel/atmel-isc.c
index fa68fe912c95..ff526022e103 100644
--- a/drivers/media/platform/atmel/atmel-isc.c
+++ b/drivers/media/platform/atmel/atmel-isc.c
@@ -29,6 +29,7 @@
 #include <linux/clk-provider.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/math64.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
@@ -36,7 +37,9 @@
 #include <linux/regmap.h>
 #include <linux/videodev2.h>
 
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
 #include <media/v4l2-image-sizes.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-of.h>
@@ -89,10 +92,12 @@ struct isc_subdev_entity {
  * struct isc_format - ISC media bus format information
  * @fourcc:		Fourcc code for this format
  * @mbus_code:		V4L2 media bus format code.
- * @bpp:		Bytes per pixel (when stored in memory)
+ * @bpp:		Bits per pixel (when stored in memory)
  * @reg_bps:		reg value for bits per sample
  *			(when transferred over a bus)
- * @support:		Indicates format supported by subdev
+ * @pipeline:		pipeline switch
+ * @sd_support:		Subdev supports this format
+ * @isc_support:	ISC can convert raw format to this format
  */
 struct isc_format {
 	u32	fourcc;
@@ -100,11 +105,42 @@ struct isc_format {
 	u8	bpp;
 
 	u32	reg_bps;
+	u32	reg_bay_cfg;
 	u32	reg_rlp_mode;
 	u32	reg_dcfg_imode;
 	u32	reg_dctrl_dview;
 
-	bool	support;
+	u32	pipeline;
+
+	bool	sd_support;
+	bool	isc_support;
+};
+
+
+#define HIST_ENTRIES		512
+#define HIST_BAYER		(ISC_HIS_CFG_MODE_B + 1)
+
+enum{
+	HIST_INIT = 0,
+	HIST_ENABLED,
+	HIST_DISABLED,
+};
+
+struct isc_ctrls {
+	struct v4l2_ctrl_handler handler;
+
+	u32 brightness;
+	u32 contrast;
+	u8 gamma_index;
+	u8 awb;
+
+	u32 r_gain;
+	u32 b_gain;
+
+	u32 hist_entry[HIST_ENTRIES];
+	u32 hist_count[HIST_BAYER];
+	u8 hist_id;
+	u8 hist_stat;
 };
 
 #define ISC_PIPE_LINE_NODE_NUM	11
@@ -131,6 +167,10 @@ struct isc_device {
 	struct isc_format	**user_formats;
 	unsigned int		num_user_formats;
 	const struct isc_format	*current_fmt;
+	const struct isc_format	*raw_fmt;
+
+	struct isc_ctrls	ctrls;
+	struct work_struct	awb_work;
 
 	struct mutex		lock;
 
@@ -140,51 +180,134 @@ struct isc_device {
 	struct list_head		subdev_entities;
 };
 
+#define RAW_FMT_INDEX_START	0
+#define RAW_FMT_INDEX_END	11
+#define ISC_FMT_INDEX_START	12
+#define ISC_FMT_INDEX_END	14
+
 static struct isc_format isc_formats[] = {
-	{ V4L2_PIX_FMT_SBGGR8, MEDIA_BUS_FMT_SBGGR8_1X8,
-	  1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
-	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SGBRG8, MEDIA_BUS_FMT_SGBRG8_1X8,
-	  1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
-	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SGRBG8, MEDIA_BUS_FMT_SGRBG8_1X8,
-	  1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
-	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SRGGB8, MEDIA_BUS_FMT_SRGGB8_1X8,
-	  1, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
-	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
-
-	{ V4L2_PIX_FMT_SBGGR10, MEDIA_BUS_FMT_SBGGR10_1X10,
-	  2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SGBRG10, MEDIA_BUS_FMT_SGBRG10_1X10,
-	  2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SGRBG10, MEDIA_BUS_FMT_SGRBG10_1X10,
-	  2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SRGGB10, MEDIA_BUS_FMT_SRGGB10_1X10,
-	  2, ISC_PFG_CFG0_BPS_TEN, ISC_RLP_CFG_MODE_DAT10,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-
-	{ V4L2_PIX_FMT_SBGGR12, MEDIA_BUS_FMT_SBGGR12_1X12,
-	  2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SGBRG12, MEDIA_BUS_FMT_SGBRG12_1X12,
-	  2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SGRBG12, MEDIA_BUS_FMT_SGRBG12_1X12,
-	  2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-	{ V4L2_PIX_FMT_SRGGB12, MEDIA_BUS_FMT_SRGGB12_1X12,
-	  2, ISC_PFG_CFG0_BPS_TWELVE, ISC_RLP_CFG_MODE_DAT12,
-	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, false },
-
-	{ V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_YUYV8_2X8,
-	  2, ISC_PFE_CFG0_BPS_EIGHT, ISC_RLP_CFG_MODE_DAT8,
-	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, false },
+	{ V4L2_PIX_FMT_SBGGR8, MEDIA_BUS_FMT_SBGGR8_1X8, 8,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_DAT8,
+	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SGBRG8, MEDIA_BUS_FMT_SGBRG8_1X8, 8,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_GBGB, ISC_RLP_CFG_MODE_DAT8,
+	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SGRBG8, MEDIA_BUS_FMT_SGRBG8_1X8, 8,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_GRGR, ISC_RLP_CFG_MODE_DAT8,
+	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SRGGB8, MEDIA_BUS_FMT_SRGGB8_1X8, 8,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_RGRG, ISC_RLP_CFG_MODE_DAT8,
+	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+
+	{ V4L2_PIX_FMT_SBGGR10, MEDIA_BUS_FMT_SBGGR10_1X10, 16,
+	  ISC_PFG_CFG0_BPS_TEN, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_DAT10,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SGBRG10, MEDIA_BUS_FMT_SGBRG10_1X10, 16,
+	  ISC_PFG_CFG0_BPS_TEN, ISC_BAY_CFG_GBGB, ISC_RLP_CFG_MODE_DAT10,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SGRBG10, MEDIA_BUS_FMT_SGRBG10_1X10, 16,
+	  ISC_PFG_CFG0_BPS_TEN, ISC_BAY_CFG_GRGR, ISC_RLP_CFG_MODE_DAT10,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SRGGB10, MEDIA_BUS_FMT_SRGGB10_1X10, 16,
+	  ISC_PFG_CFG0_BPS_TEN, ISC_BAY_CFG_RGRG, ISC_RLP_CFG_MODE_DAT10,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+
+	{ V4L2_PIX_FMT_SBGGR12, MEDIA_BUS_FMT_SBGGR12_1X12, 16,
+	  ISC_PFG_CFG0_BPS_TWELVE, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_DAT12,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SGBRG12, MEDIA_BUS_FMT_SGBRG12_1X12, 16,
+	  ISC_PFG_CFG0_BPS_TWELVE, ISC_BAY_CFG_GBGB, ISC_RLP_CFG_MODE_DAT12,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SGRBG12, MEDIA_BUS_FMT_SGRBG12_1X12, 16,
+	  ISC_PFG_CFG0_BPS_TWELVE, ISC_BAY_CFG_GRGR, ISC_RLP_CFG_MODE_DAT12,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+	{ V4L2_PIX_FMT_SRGGB12, MEDIA_BUS_FMT_SRGGB12_1X12, 16,
+	  ISC_PFG_CFG0_BPS_TWELVE, ISC_BAY_CFG_RGRG, ISC_RLP_CFG_MODE_DAT12,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+
+	{ V4L2_PIX_FMT_YUV420, 0x0, 12,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_YYCC,
+	  ISC_DCFG_IMODE_YC420P | ISC_DCFG_YMBSIZE_BEATS8 |
+	  ISC_DCFG_CMBSIZE_BEATS8, ISC_DCTRL_DVIEW_PLANAR, 0x7fb,
+	  false, false },
+	{ V4L2_PIX_FMT_YUV422P, 0x0, 16,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_YYCC,
+	  ISC_DCFG_IMODE_YC422P | ISC_DCFG_YMBSIZE_BEATS8 |
+	  ISC_DCFG_CMBSIZE_BEATS8, ISC_DCTRL_DVIEW_PLANAR, 0x3fb,
+	  false, false },
+	{ V4L2_PIX_FMT_RGB565, MEDIA_BUS_FMT_RGB565_2X8_LE, 16,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_RGB565,
+	  ISC_DCFG_IMODE_PACKED16, ISC_DCTRL_DVIEW_PACKED, 0x7b,
+	  false, false },
+
+	{ V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_YUYV8_2X8, 16,
+	  ISC_PFE_CFG0_BPS_EIGHT, ISC_BAY_CFG_BGBG, ISC_RLP_CFG_MODE_DAT8,
+	  ISC_DCFG_IMODE_PACKED8, ISC_DCTRL_DVIEW_PACKED, 0x0,
+	  false, false },
+};
+
+#define GAMMA_MAX	3
+#define GAMMA_ENTRIES	64
+
+/* Gamma table with gamma 1/2.2 */
+static const u32 isc_gamma_table[GAMMA_MAX][GAMMA_ENTRIES] = {
+	/* 0 --> gamma 1/1.8 */
+	{      0x65,  0x66002F,  0x950025,  0xBB0020,  0xDB001D,  0xF8001A,
+	  0x1130018, 0x12B0017, 0x1420016, 0x1580014, 0x16D0013, 0x1810012,
+	  0x1940012, 0x1A60012, 0x1B80011, 0x1C90010, 0x1DA0010, 0x1EA000F,
+	  0x1FA000F, 0x209000F, 0x218000F, 0x227000E, 0x235000E, 0x243000E,
+	  0x251000E, 0x25F000D, 0x26C000D, 0x279000D, 0x286000D, 0x293000C,
+	  0x2A0000C, 0x2AC000C, 0x2B8000C, 0x2C4000C, 0x2D0000B, 0x2DC000B,
+	  0x2E7000B, 0x2F3000B, 0x2FE000B, 0x309000B, 0x314000B, 0x31F000A,
+	  0x32A000A, 0x334000B, 0x33F000A, 0x349000A, 0x354000A, 0x35E000A,
+	  0x368000A, 0x372000A, 0x37C000A, 0x386000A, 0x3900009, 0x399000A,
+	  0x3A30009, 0x3AD0009, 0x3B60009, 0x3BF000A, 0x3C90009, 0x3D20009,
+	  0x3DB0009, 0x3E40009, 0x3ED0009, 0x3F60009 },
+
+	/* 1 --> gamma 1/2 */
+	{      0x7F,  0x800034,  0xB50028,  0xDE0021, 0x100001E, 0x11E001B,
+	  0x1390019, 0x1520017, 0x16A0015, 0x1800014, 0x1940014, 0x1A80013,
+	  0x1BB0012, 0x1CD0011, 0x1DF0010, 0x1EF0010, 0x200000F, 0x20F000F,
+	  0x21F000E, 0x22D000F, 0x23C000E, 0x24A000E, 0x258000D, 0x265000D,
+	  0x273000C, 0x27F000D, 0x28C000C, 0x299000C, 0x2A5000C, 0x2B1000B,
+	  0x2BC000C, 0x2C8000B, 0x2D3000C, 0x2DF000B, 0x2EA000A, 0x2F5000A,
+	  0x2FF000B, 0x30A000A, 0x314000B, 0x31F000A, 0x329000A, 0x333000A,
+	  0x33D0009, 0x3470009, 0x350000A, 0x35A0009, 0x363000A, 0x36D0009,
+	  0x3760009, 0x37F0009, 0x3880009, 0x3910009, 0x39A0009, 0x3A30009,
+	  0x3AC0008, 0x3B40009, 0x3BD0008, 0x3C60008, 0x3CE0008, 0x3D60009,
+	  0x3DF0008, 0x3E70008, 0x3EF0008, 0x3F70008 },
+
+	/* 2 --> gamma 1/2.2 */
+	{      0x99,  0x9B0038,  0xD4002A,  0xFF0023, 0x122001F, 0x141001B,
+	  0x15D0019, 0x1760017, 0x18E0015, 0x1A30015, 0x1B80013, 0x1CC0012,
+	  0x1DE0011, 0x1F00010, 0x2010010, 0x2110010, 0x221000F, 0x230000F,
+	  0x23F000E, 0x24D000E, 0x25B000D, 0x269000C, 0x276000C, 0x283000C,
+	  0x28F000C, 0x29B000C, 0x2A7000C, 0x2B3000B, 0x2BF000B, 0x2CA000B,
+	  0x2D5000B, 0x2E0000A, 0x2EB000A, 0x2F5000A, 0x2FF000A, 0x30A000A,
+	  0x3140009, 0x31E0009, 0x327000A, 0x3310009, 0x33A0009, 0x3440009,
+	  0x34D0009, 0x3560009, 0x35F0009, 0x3680008, 0x3710008, 0x3790009,
+	  0x3820008, 0x38A0008, 0x3930008, 0x39B0008, 0x3A30008, 0x3AB0008,
+	  0x3B30008, 0x3BB0008, 0x3C30008, 0x3CB0007, 0x3D20008, 0x3DA0007,
+	  0x3E20007, 0x3E90007, 0x3F00008, 0x3F80007 },
 };
 
+static unsigned int sensor_preferred = 1;
+module_param(sensor_preferred, uint, 0644);
+MODULE_PARM_DESC(sensor_preferred,
+		 "Sensor is preferred to output the specified format (1-on 0-off), default 1");
+
 static int isc_clk_enable(struct clk_hw *hw)
 {
 	struct isc_clk *isc_clk = to_isc_clk(hw);
@@ -447,27 +570,158 @@ static int isc_buffer_prepare(struct vb2_buffer *vb)
 	return 0;
 }
 
-static inline void isc_start_dma(struct regmap *regmap,
-				  struct isc_buffer *frm, u32 dview)
+static inline bool sensor_is_preferred(const struct isc_format *isc_fmt)
+{
+	if ((sensor_preferred && isc_fmt->sd_support) ||
+	    !isc_fmt->isc_support)
+		return true;
+	else
+		return false;
+}
+
+static void isc_start_dma(struct isc_device *isc)
 {
-	dma_addr_t addr;
+	struct regmap *regmap = isc->regmap;
+	struct v4l2_pix_format *pixfmt = &isc->fmt.fmt.pix;
+	u32 sizeimage = pixfmt->sizeimage;
+	u32 dctrl_dview;
+	dma_addr_t addr0;
+
+	addr0 = vb2_dma_contig_plane_dma_addr(&isc->cur_frm->vb.vb2_buf, 0);
+	regmap_write(regmap, ISC_DAD0, addr0);
+
+	switch (pixfmt->pixelformat) {
+	case V4L2_PIX_FMT_YUV420:
+		regmap_write(regmap, ISC_DAD1, addr0 + (sizeimage*2)/3);
+		regmap_write(regmap, ISC_DAD2, addr0 + (sizeimage*5)/6);
+		break;
+	case V4L2_PIX_FMT_YUV422P:
+		regmap_write(regmap, ISC_DAD1, addr0 + sizeimage/2);
+		regmap_write(regmap, ISC_DAD2, addr0 + (sizeimage*3)/4);
+		break;
+	default:
+		break;
+	}
 
-	addr = vb2_dma_contig_plane_dma_addr(&frm->vb.vb2_buf, 0);
+	if (sensor_is_preferred(isc->current_fmt))
+		dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
+	else
+		dctrl_dview = isc->current_fmt->reg_dctrl_dview;
 
-	regmap_write(regmap, ISC_DCTRL, dview | ISC_DCTRL_IE_IS);
-	regmap_write(regmap, ISC_DAD0, addr);
+	regmap_write(regmap, ISC_DCTRL, dctrl_dview | ISC_DCTRL_IE_IS);
 	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_CAPTURE);
 }
 
 static void isc_set_pipeline(struct isc_device *isc, u32 pipeline)
 {
-	u32 val;
+	struct regmap *regmap = isc->regmap;
+	struct isc_ctrls *ctrls = &isc->ctrls;
+	u32 val, bay_cfg;
+	const u32 *gamma;
 	unsigned int i;
 
+	/* WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB422-->SUB420 */
 	for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) {
 		val = pipeline & BIT(i) ? 1 : 0;
 		regmap_field_write(isc->pipeline[i], val);
 	}
+
+	if (!pipeline)
+		return;
+
+	bay_cfg = isc->raw_fmt->reg_bay_cfg;
+
+	regmap_write(regmap, ISC_WB_CFG, bay_cfg);
+	regmap_write(regmap, ISC_WB_O_RGR, 0x0);
+	regmap_write(regmap, ISC_WB_O_BGR, 0x0);
+	regmap_write(regmap, ISC_WB_G_RGR, ctrls->r_gain | (0x1 << 25));
+	regmap_write(regmap, ISC_WB_G_BGR, ctrls->b_gain | (0x1 << 25));
+
+	regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL);
+
+	gamma = &isc_gamma_table[ctrls->gamma_index][0];
+	regmap_bulk_write(regmap, ISC_GAM_BENTRY, gamma, GAMMA_ENTRIES);
+	regmap_bulk_write(regmap, ISC_GAM_GENTRY, gamma, GAMMA_ENTRIES);
+	regmap_bulk_write(regmap, ISC_GAM_RENTRY, gamma, GAMMA_ENTRIES);
+
+	/* Convert RGB to YUV */
+	regmap_write(regmap, ISC_CSC_YR_YG, 0x42 | (0x81 << 16));
+	regmap_write(regmap, ISC_CSC_YB_OY, 0x19 | (0x10 << 16));
+	regmap_write(regmap, ISC_CSC_CBR_CBG, 0xFDA | (0xFB6 << 16));
+	regmap_write(regmap, ISC_CSC_CBB_OCB, 0x70 | (0x80 << 16));
+	regmap_write(regmap, ISC_CSC_CRR_CRG, 0x70 | (0xFA2 << 16));
+	regmap_write(regmap, ISC_CSC_CRB_OCR, 0xFEE | (0x80 << 16));
+
+	regmap_write(regmap, ISC_CBC_BRIGHT, ctrls->brightness);
+	regmap_write(regmap, ISC_CBC_CONTRAST, ctrls->contrast);
+}
+
+static int isc_update_profile(struct isc_device *isc)
+{
+	struct regmap *regmap = isc->regmap;
+	u32 sr;
+	int counter = 100;
+
+	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO);
+
+	regmap_read(regmap, ISC_CTRLSR, &sr);
+	while ((sr & ISC_CTRL_UPPRO) && counter--) {
+		usleep_range(1000, 2000);
+		regmap_read(regmap, ISC_CTRLSR, &sr);
+	}
+
+	if (counter < 0) {
+		v4l2_warn(&isc->v4l2_dev, "Time out to update profie\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static void isc_set_histogram(struct isc_device *isc)
+{
+	struct regmap *regmap = isc->regmap;
+	struct isc_ctrls *ctrls = &isc->ctrls;
+
+	if (ctrls->awb && (ctrls->hist_stat != HIST_ENABLED)) {
+		regmap_write(regmap, ISC_HIS_CFG, ISC_HIS_CFG_MODE_R |
+		      (isc->raw_fmt->reg_bay_cfg << ISC_HIS_CFG_BAYSEL_SHIFT) |
+		      ISC_HIS_CFG_RAR);
+		regmap_write(regmap, ISC_HIS_CTRL, ISC_HIS_CTRL_EN);
+		regmap_write(regmap, ISC_INTEN, ISC_INT_HISDONE);
+		ctrls->hist_id = ISC_HIS_CFG_MODE_R;
+		isc_update_profile(isc);
+		regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
+
+		ctrls->hist_stat = HIST_ENABLED;
+	} else if (!ctrls->awb && (ctrls->hist_stat != HIST_DISABLED)) {
+		regmap_write(regmap, ISC_INTDIS, ISC_INT_HISDONE);
+		regmap_write(regmap, ISC_HIS_CTRL, ISC_HIS_CTRL_DIS);
+
+		ctrls->hist_stat = HIST_DISABLED;
+	}
+}
+
+static inline void isc_get_param(const struct isc_format *fmt,
+				     u32 *rlp_mode, u32 *dcfg_imode)
+{
+	switch (fmt->fourcc) {
+	case V4L2_PIX_FMT_SBGGR10:
+	case V4L2_PIX_FMT_SGBRG10:
+	case V4L2_PIX_FMT_SGRBG10:
+	case V4L2_PIX_FMT_SRGGB10:
+	case V4L2_PIX_FMT_SBGGR12:
+	case V4L2_PIX_FMT_SGBRG12:
+	case V4L2_PIX_FMT_SGRBG12:
+	case V4L2_PIX_FMT_SRGGB12:
+		*rlp_mode = fmt->reg_rlp_mode;
+		*dcfg_imode = fmt->reg_dcfg_imode;
+		break;
+	default:
+		*rlp_mode = ISC_RLP_CFG_MODE_DAT8;
+		*dcfg_imode = ISC_DCFG_IMODE_PACKED8;
+		break;
+	}
 }
 
 static int isc_configure(struct isc_device *isc)
@@ -475,39 +729,40 @@ static int isc_configure(struct isc_device *isc)
 	struct regmap *regmap = isc->regmap;
 	const struct isc_format *current_fmt = isc->current_fmt;
 	struct isc_subdev_entity *subdev = isc->current_subdev;
-	u32 val, mask;
-	int counter = 10;
+	u32 pfe_cfg0, rlp_mode, dcfg_imode, mask, pipeline;
+
+	if (sensor_is_preferred(current_fmt)) {
+		pfe_cfg0 = current_fmt->reg_bps;
+		pipeline = 0x0;
+		isc_get_param(current_fmt, &rlp_mode, &dcfg_imode);
+		isc->ctrls.hist_stat = HIST_INIT;
+	} else {
+		pfe_cfg0  = isc->raw_fmt->reg_bps;
+		pipeline = current_fmt->pipeline;
+		rlp_mode = current_fmt->reg_rlp_mode;
+		dcfg_imode = current_fmt->reg_dcfg_imode;
+	}
 
-	val = current_fmt->reg_bps | subdev->pfe_cfg0 |
-	      ISC_PFE_CFG0_MODE_PROGRESSIVE;
+	pfe_cfg0  |= subdev->pfe_cfg0 | ISC_PFE_CFG0_MODE_PROGRESSIVE;
 	mask = ISC_PFE_CFG0_BPS_MASK | ISC_PFE_CFG0_HPOL_LOW |
 	       ISC_PFE_CFG0_VPOL_LOW | ISC_PFE_CFG0_PPOL_LOW |
 	       ISC_PFE_CFG0_MODE_MASK;
 
-	regmap_update_bits(regmap, ISC_PFE_CFG0, mask, val);
+	regmap_update_bits(regmap, ISC_PFE_CFG0, mask, pfe_cfg0);
 
 	regmap_update_bits(regmap, ISC_RLP_CFG, ISC_RLP_CFG_MODE_MASK,
-			   current_fmt->reg_rlp_mode);
+			   rlp_mode);
 
-	regmap_update_bits(regmap, ISC_DCFG, ISC_DCFG_IMODE_MASK,
-			   current_fmt->reg_dcfg_imode);
+	regmap_update_bits(regmap, ISC_DCFG, ISC_DCFG_IMODE_MASK, dcfg_imode);
 
-	/* Disable the pipeline */
-	isc_set_pipeline(isc, 0x0);
+	/* Set the pipeline */
+	isc_set_pipeline(isc, pipeline);
 
-	/* Update profile */
-	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO);
+	if (pipeline)
+		isc_set_histogram(isc);
 
-	regmap_read(regmap, ISC_CTRLSR, &val);
-	while ((val & ISC_CTRL_UPPRO) && counter--) {
-		usleep_range(1000, 2000);
-		regmap_read(regmap, ISC_CTRLSR, &val);
-	}
-
-	if (counter < 0)
-		return -ETIMEDOUT;
-
-	return 0;
+	/* Update profile */
+	return isc_update_profile(isc);
 }
 
 static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
@@ -517,7 +772,6 @@ static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
 	struct isc_buffer *buf;
 	unsigned long flags;
 	int ret;
-	u32 val;
 
 	/* Enable stream on the sub device */
 	ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 1);
@@ -528,12 +782,6 @@ static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
 
 	pm_runtime_get_sync(isc->dev);
 
-	/* Disable all the interrupts */
-	regmap_write(isc->regmap, ISC_INTDIS, (u32)~0UL);
-
-	/* Clean the interrupt status register */
-	regmap_read(regmap, ISC_INTSR, &val);
-
 	ret = isc_configure(isc);
 	if (unlikely(ret))
 		goto err_configure;
@@ -551,7 +799,7 @@ static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
 					struct isc_buffer, list);
 	list_del(&isc->cur_frm->list);
 
-	isc_start_dma(regmap, isc->cur_frm, isc->current_fmt->reg_dctrl_dview);
+	isc_start_dma(isc);
 
 	spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
 
@@ -620,8 +868,7 @@ static void isc_buffer_queue(struct vb2_buffer *vb)
 	if (!isc->cur_frm && list_empty(&isc->dma_queue) &&
 		vb2_is_streaming(vb->vb2_queue)) {
 		isc->cur_frm = buf;
-		isc_start_dma(isc->regmap, isc->cur_frm,
-			isc->current_fmt->reg_dctrl_dview);
+		isc_start_dma(isc);
 	} else
 		list_add_tail(&buf->list, &isc->dma_queue);
 	spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
@@ -691,13 +938,14 @@ static struct isc_format *find_format_by_fourcc(struct isc_device *isc,
 }
 
 static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f,
-			struct isc_format **current_fmt)
+			struct isc_format **current_fmt, u32 *code)
 {
 	struct isc_format *isc_fmt;
 	struct v4l2_pix_format *pixfmt = &f->fmt.pix;
 	struct v4l2_subdev_format format = {
 		.which = V4L2_SUBDEV_FORMAT_TRY,
 	};
+	u32 mbus_code;
 	int ret;
 
 	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -717,7 +965,12 @@ static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f,
 	if (pixfmt->height > ISC_MAX_SUPPORT_HEIGHT)
 		pixfmt->height = ISC_MAX_SUPPORT_HEIGHT;
 
-	v4l2_fill_mbus_format(&format.format, pixfmt, isc_fmt->mbus_code);
+	if (sensor_is_preferred(isc_fmt))
+		mbus_code = isc_fmt->mbus_code;
+	else
+		mbus_code = isc->raw_fmt->mbus_code;
+
+	v4l2_fill_mbus_format(&format.format, pixfmt, mbus_code);
 	ret = v4l2_subdev_call(isc->current_subdev->sd, pad, set_fmt,
 			       isc->current_subdev->config, &format);
 	if (ret < 0)
@@ -726,12 +979,15 @@ static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f,
 	v4l2_fill_pix_format(pixfmt, &format.format);
 
 	pixfmt->field = V4L2_FIELD_NONE;
-	pixfmt->bytesperline = pixfmt->width * isc_fmt->bpp;
+	pixfmt->bytesperline = (pixfmt->width * isc_fmt->bpp) >> 3;
 	pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
 
 	if (current_fmt)
 		*current_fmt = isc_fmt;
 
+	if (code)
+		*code = mbus_code;
+
 	return 0;
 }
 
@@ -741,14 +997,14 @@ static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f)
 		.which = V4L2_SUBDEV_FORMAT_ACTIVE,
 	};
 	struct isc_format *current_fmt;
+	u32 mbus_code;
 	int ret;
 
-	ret = isc_try_fmt(isc, f, &current_fmt);
+	ret = isc_try_fmt(isc, f, &current_fmt, &mbus_code);
 	if (ret)
 		return ret;
 
-	v4l2_fill_mbus_format(&format.format, &f->fmt.pix,
-			      current_fmt->mbus_code);
+	v4l2_fill_mbus_format(&format.format, &f->fmt.pix, mbus_code);
 	ret = v4l2_subdev_call(isc->current_subdev->sd, pad,
 			       set_fmt, NULL, &format);
 	if (ret < 0)
@@ -776,7 +1032,7 @@ static int isc_try_fmt_vid_cap(struct file *file, void *priv,
 {
 	struct isc_device *isc = video_drvdata(file);
 
-	return isc_try_fmt(isc, f, NULL);
+	return isc_try_fmt(isc, f, NULL, NULL);
 }
 
 static int isc_enum_input(struct file *file, void *priv,
@@ -842,7 +1098,10 @@ static int isc_enum_framesizes(struct file *file, void *fh,
 	if (!isc_fmt)
 		return -EINVAL;
 
-	fse.code = isc_fmt->mbus_code;
+	if (sensor_is_preferred(isc_fmt))
+		fse.code = isc_fmt->mbus_code;
+	else
+		fse.code = isc->raw_fmt->mbus_code;
 
 	ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size,
 			       NULL, &fse);
@@ -873,7 +1132,10 @@ static int isc_enum_frameintervals(struct file *file, void *fh,
 	if (!isc_fmt)
 		return -EINVAL;
 
-	fie.code = isc_fmt->mbus_code;
+	if (sensor_is_preferred(isc_fmt))
+		fie.code = isc_fmt->mbus_code;
+	else
+		fie.code = isc->raw_fmt->mbus_code;
 
 	ret = v4l2_subdev_call(isc->current_subdev->sd, pad,
 			       enum_frame_interval, NULL, &fie);
@@ -911,6 +1173,10 @@ static const struct v4l2_ioctl_ops isc_ioctl_ops = {
 	.vidioc_s_parm			= isc_s_parm,
 	.vidioc_enum_framesizes		= isc_enum_framesizes,
 	.vidioc_enum_frameintervals	= isc_enum_frameintervals,
+
+	.vidioc_log_status		= v4l2_ctrl_log_status,
+	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
 };
 
 static int isc_open(struct file *file)
@@ -984,14 +1250,13 @@ static irqreturn_t isc_interrupt(int irq, void *dev_id)
 	u32 isc_intsr, isc_intmask, pending;
 	irqreturn_t ret = IRQ_NONE;
 
-	spin_lock(&isc->dma_queue_lock);
-
 	regmap_read(regmap, ISC_INTSR, &isc_intsr);
 	regmap_read(regmap, ISC_INTMASK, &isc_intmask);
 
 	pending = isc_intsr & isc_intmask;
 
 	if (likely(pending & ISC_INT_DDONE)) {
+		spin_lock(&isc->dma_queue_lock);
 		if (isc->cur_frm) {
 			struct vb2_v4l2_buffer *vbuf = &isc->cur_frm->vb;
 			struct vb2_buffer *vb = &vbuf->vb2_buf;
@@ -1007,21 +1272,144 @@ static irqreturn_t isc_interrupt(int irq, void *dev_id)
 						     struct isc_buffer, list);
 			list_del(&isc->cur_frm->list);
 
-			isc_start_dma(regmap, isc->cur_frm,
-				      isc->current_fmt->reg_dctrl_dview);
+			isc_start_dma(isc);
 		}
 
 		if (isc->stop)
 			complete(&isc->comp);
 
 		ret = IRQ_HANDLED;
+		spin_unlock(&isc->dma_queue_lock);
 	}
 
-	spin_unlock(&isc->dma_queue_lock);
+	if (pending & ISC_INT_HISDONE) {
+		schedule_work(&isc->awb_work);
+		ret = IRQ_HANDLED;
+	}
 
 	return ret;
 }
 
+static void isc_hist_count(struct isc_device *isc)
+{
+	struct regmap *regmap = isc->regmap;
+	struct isc_ctrls *ctrls = &isc->ctrls;
+	u32 *hist_count = &ctrls->hist_count[ctrls->hist_id];
+	u32 *hist_entry = &ctrls->hist_entry[0];
+	u32 i;
+
+	regmap_bulk_read(regmap, ISC_HIS_ENTRY, hist_entry, HIST_ENTRIES);
+
+	*hist_count = 0;
+	for (i = 0; i <= HIST_ENTRIES; i++)
+		*hist_count += i * (*hist_entry++);
+}
+
+static void isc_wb_update(struct isc_ctrls *ctrls)
+{
+	u32 *hist_count = &ctrls->hist_count[0];
+	u64 g_count = (u64)hist_count[ISC_HIS_CFG_MODE_GB] << 9;
+	u32 hist_r = hist_count[ISC_HIS_CFG_MODE_R];
+	u32 hist_b = hist_count[ISC_HIS_CFG_MODE_B];
+
+	if (hist_r)
+		ctrls->r_gain = div_u64(g_count, hist_r);
+
+	if (hist_b)
+		ctrls->b_gain = div_u64(g_count, hist_b);
+}
+
+static void isc_awb_work(struct work_struct *w)
+{
+	struct isc_device *isc =
+		container_of(w, struct isc_device, awb_work);
+	struct regmap *regmap = isc->regmap;
+	struct isc_ctrls *ctrls = &isc->ctrls;
+	u32 hist_id = ctrls->hist_id;
+	u32 baysel;
+
+	if (ctrls->hist_stat != HIST_ENABLED)
+		return;
+
+	isc_hist_count(isc);
+
+	if (hist_id != ISC_HIS_CFG_MODE_B) {
+		hist_id++;
+	} else {
+		isc_wb_update(ctrls);
+		hist_id = ISC_HIS_CFG_MODE_R;
+	}
+
+	ctrls->hist_id = hist_id;
+	baysel = isc->raw_fmt->reg_bay_cfg << ISC_HIS_CFG_BAYSEL_SHIFT;
+
+	pm_runtime_get_sync(isc->dev);
+
+	regmap_write(regmap, ISC_HIS_CFG, hist_id | baysel | ISC_HIS_CFG_RAR);
+	isc_update_profile(isc);
+	regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
+
+	pm_runtime_put_sync(isc->dev);
+}
+
+static int isc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct isc_device *isc = container_of(ctrl->handler,
+					     struct isc_device, ctrls.handler);
+	struct isc_ctrls *ctrls = &isc->ctrls;
+
+	switch (ctrl->id) {
+	case V4L2_CID_BRIGHTNESS:
+		ctrls->brightness = ctrl->val & ISC_CBC_BRIGHT_MASK;
+		break;
+	case V4L2_CID_CONTRAST:
+		ctrls->contrast = (ctrl->val << 8) & ISC_CBC_CONTRAST_MASK;
+		break;
+	case V4L2_CID_GAMMA:
+		ctrls->gamma_index = ctrl->val;
+		break;
+	case V4L2_CID_AUTO_WHITE_BALANCE:
+		ctrls->awb = ctrl->val;
+		if (ctrls->hist_stat != HIST_ENABLED) {
+			ctrls->r_gain = 0x1 << 9;
+			ctrls->b_gain = 0x1 << 9;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct v4l2_ctrl_ops isc_ctrl_ops = {
+	.s_ctrl	= isc_s_ctrl,
+};
+
+static int isc_ctrl_init(struct isc_device *isc)
+{
+	const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops;
+	struct isc_ctrls *ctrls = &isc->ctrls;
+	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
+	int ret;
+
+	ctrls->hist_stat = HIST_INIT;
+
+	ret = v4l2_ctrl_handler_init(hdl, 4);
+	if (ret < 0)
+		return ret;
+
+	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0);
+	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -8, 7, 1, 1);
+	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, GAMMA_MAX - 1, 1, 2);
+	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+
+	v4l2_ctrl_handler_setup(hdl);
+
+	return 0;
+}
+
+
 static int isc_async_bound(struct v4l2_async_notifier *notifier,
 			    struct v4l2_subdev *subdev,
 			    struct v4l2_async_subdev *asd)
@@ -1047,10 +1435,11 @@ static void isc_async_unbind(struct v4l2_async_notifier *notifier,
 {
 	struct isc_device *isc = container_of(notifier->v4l2_dev,
 					      struct isc_device, v4l2_dev);
-
+	cancel_work_sync(&isc->awb_work);
 	video_unregister_device(&isc->video_dev);
 	if (isc->current_subdev->config)
 		v4l2_subdev_free_pad_config(isc->current_subdev->config);
+	v4l2_ctrl_handler_free(&isc->ctrls.handler);
 }
 
 static struct isc_format *find_format_by_code(unsigned int code, int *index)
@@ -1081,7 +1470,9 @@ static int isc_formats_init(struct isc_device *isc)
 
 	fmt = &isc_formats[0];
 	for (i = 0; i < ARRAY_SIZE(isc_formats); i++) {
-		fmt->support = false;
+		fmt->isc_support = false;
+		fmt->sd_support = false;
+
 		fmt++;
 	}
 
@@ -1092,8 +1483,22 @@ static int isc_formats_init(struct isc_device *isc)
 		if (!fmt)
 			continue;
 
-		fmt->support = true;
-		num_fmts++;
+		fmt->sd_support = true;
+
+		if (i <= RAW_FMT_INDEX_END) {
+			for (j = ISC_FMT_INDEX_START;
+			     j <= ISC_FMT_INDEX_END; j++)
+				isc_formats[j].isc_support = true;
+
+			isc->raw_fmt = fmt;
+		}
+	}
+
+	for (i = 0, num_fmts = 0; i < ARRAY_SIZE(isc_formats); i++) {
+		if (fmt->isc_support || fmt->sd_support)
+			num_fmts++;
+
+		fmt++;
 	}
 
 	if (!num_fmts)
@@ -1110,7 +1515,7 @@ static int isc_formats_init(struct isc_device *isc)
 
 	fmt = &isc_formats[0];
 	for (i = 0, j = 0; i < ARRAY_SIZE(isc_formats); i++) {
-		if (fmt->support)
+		if (fmt->isc_support || fmt->sd_support)
 			isc->user_formats[j++] = fmt;
 
 		fmt++;
@@ -1132,7 +1537,7 @@ static int isc_set_default_fmt(struct isc_device *isc)
 	};
 	int ret;
 
-	ret = isc_try_fmt(isc, &f, NULL);
+	ret = isc_try_fmt(isc, &f, NULL, NULL);
 	if (ret)
 		return ret;
 
@@ -1151,6 +1556,12 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
 	struct vb2_queue *q = &isc->vb2_vidq;
 	int ret;
 
+	ret = v4l2_device_register_subdev_nodes(&isc->v4l2_dev);
+	if (ret < 0) {
+		v4l2_err(&isc->v4l2_dev, "Failed to register subdev nodes\n");
+		return ret;
+	}
+
 	isc->current_subdev = container_of(notifier,
 					   struct isc_subdev_entity, notifier);
 	sd_entity = isc->current_subdev;
@@ -1198,6 +1609,14 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
 		return ret;
 	}
 
+	ret = isc_ctrl_init(isc);
+	if (ret) {
+		v4l2_err(&isc->v4l2_dev, "Init isc ctrols failed: %d\n", ret);
+		return ret;
+	}
+
+	INIT_WORK(&isc->awb_work, isc_awb_work);
+
 	/* Register video device */
 	strlcpy(vdev->name, ATMEL_ISC_NAME, sizeof(vdev->name));
 	vdev->release		= video_device_release_empty;
@@ -1207,7 +1626,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
 	vdev->vfl_dir		= VFL_DIR_RX;
 	vdev->queue		= q;
 	vdev->lock		= &isc->lock;
-	vdev->ctrl_handler	= isc->current_subdev->sd->ctrl_handler;
+	vdev->ctrl_handler	= &isc->ctrls.handler;
 	vdev->device_caps	= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
 	video_set_drvdata(vdev, isc);
 
-- 
2.11.0

^ permalink raw reply related

* [PATCH v1] watchdog: imx2: fix hang-up on boot for i.MX21, i.MX27 and i.MX31 SoCs
From: Vladimir Zapolskiy @ 2016-12-23  9:27 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161223083256.3wdqou5xmhr7fidn@pengutronix.de>

On 12/23/2016 10:32 AM, Uwe Kleine-K?nig wrote:
> Hello,
> 
> On Fri, Dec 23, 2016 at 10:20:20AM +0200, Vladimir Zapolskiy wrote:
>> On 12/23/2016 03:55 AM, Guenter Roeck wrote:
>>> What is the ultimate conclusion of this exchange ?
>>>
>>> Are we going to get another version of the patch, or did everyone agree that
>>> the patch is good as it is and does not require further changes ?
>>>
>>
>> I can not imagine a different fix.
> 
> my preferred fix would be:
> 
>  - add an imx35 compatible to all newer dtsi
>  - update the driver to only write the wmcr on imx35 compatible devices
>    adding only imx35.
> 

It breaks old DTS vs. new kernel compatibility, I've already mentioned this.

--
With best wishes,
Vladimir

^ permalink raw reply

* [RFT PATCH] ARM64: dts: meson-gxbb: Add reserved memory zone and usable memory range
From: Heinrich Schuchardt @ 2016-12-23  9:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <2ac01237-a7f8-5b3b-0f97-dd49ce6623fb@gmx.de>

On 12/22/2016 11:02 AM, Heinrich Schuchardt wrote:
> On 12/14/2016 10:52 AM, Neil Armstrong wrote:
> 
>> Hi Heinrich,
>>
>> Thanks for testing and for the report,
>> we are still struggling into finding what are these zones and how to label them correctly.
>>
>> We need to identify the zones on all boards, the patch I provided works on a non-odroid-c2 and gxm and gxl boards.
>>
>> Neil
>>
> Hello Neil,
> 
> the configuration below works for me on the Hardkernel Odroid C2.
> 
> ramoops is needed for CONFIG_PSTORE_RAM.
> Debian Stretch has CONFIG_PSTORE_RAM=m. Same is true for Fedora.
> I have chosen the address arbitrarily. To accommodate 512 MB boards we
> would have to put it below 0x20000000.
> The size parameters are the same as in hisilicon/hi6220-hikey.dts and
> qcom-apq8064-asus-nexus7-flo.dts.
> 
> linux,cma is used for contiguous memory assignment. I have taken the
> align parameter from arm-src-kernel-2016-08-18-26e194264c.tar.gz
> provided by Amlogic at
> http://openlinux.amlogic.com:8000/download/ARM/kernel/ .
> See Documentation/DMA-API.txt for the usage of align.
> They use the same value 0x400000 for all GXBB boards.
> So we want to put this zone into meson-gxbb.dtsi.
> 
> secmon is used by drivers/firmware/meson/meson_sm.c.
> Amlogic uses the same address range for all 64bit boards.
> 
> 	memory at 0 {
> 		device_type = "memory";
> 		linux,usable-memory = <0x0 0x1000000 0x0 0x7f000000>;
> 	};
> 
> 	reserved-memory {
> 		#address-cells = <0x2>;
> 		#size-cells = <0x2>;
> 		ranges;
> 
> 		ramoops at 0x23f00000 {
> 			compatible = "ramoops";
> 			reg = <0x0 0x23f00000 0x0 0x100000>;
> 			record-size = <0x20000>;
> 			console-size = <0x20000>;
> 			ftrace-size = <0x20000>;
> 		};
> 
> 		secmon: secmon {
> 			compatible = "amlogic, aml_secmon_memory";
> 			reg = <0x0 0x10000000 0x0 0x200000>;
> 			no-map;
> 		};
> 
> 		linux,cma {
> 			compatible = "shared-dma-pool";
> 			reusable;
> 			size = <0x0 0xbc00000>;
> 			alignment = <0x0 0x400000>;
> 			linux,cma-default;
> 		};
> 	};
> 
> Best regards
> 
> Heinrich Schuchardt
> 

Hello Neil,

it really makes a difference if we write

 	memory at 0 {
 		device_type = "memory";
 		linux,usable-memory = <0x0 0x1000000 0x0 0x7f000000>;
 	};

or

 	memory at 0 {
 		device_type = "memory";
 		reg = <0x0 0x1000000 0x0 0x7f000000>;
 	};

The second version leads to failure of the Odroid C2.

When I looked at /sys/firmware/fdt I saw this difference:

--- fails
+++ works

        memory at 0 {
-               device_type = "memory";
                reg = <0x0 0x0 0x0 0x78000000>;
+               device_type = "memory";
+               linux,usable-memory = <0x0 0x1000000 0x0 0x7f000000>;
        };

I found the following sentence in the NXP forum:
In case you want to overwrite the memory usage passed from u-boot, you
can use "linux,usable-memory".
https://community.nxp.com/thread/382284

Best regards

Heinrich Schuchardt

^ permalink raw reply

* [PATCH] dmaengine: pl330: Fix runtime PM support for terminated transfers
From: Marek Szyprowski @ 2016-12-23  9:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161223023324.GA3860@kozik-lap>

Hi Krzysztof,


On 2016-12-23 03:33, Krzysztof Kozlowski wrote:
> On Fri, Dec 16, 2016 at 11:39:11AM +0100, Marek Szyprowski wrote:
>> PL330 DMA engine driver is leaking a runtime reference after any terminated
>> DMA transactions. This patch fixes this issue by tracking runtime PM state
>> of the device and making additional call to pm_runtime_put() in terminate_all
>> callback if needed.
>>
>> Fixes: ae43b3289186 ("ARM: 8202/1: dmaengine: pl330: Add runtime Power Management support v12")
>> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
>> ---
>>   drivers/dma/pl330.c | 11 +++++++++++
>>   1 file changed, 11 insertions(+)
>>
>> diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
>> index 030fe05ed43b..9f3dbc8c63d2 100644
>> --- a/drivers/dma/pl330.c
>> +++ b/drivers/dma/pl330.c
>> @@ -448,6 +448,9 @@ struct dma_pl330_chan {
>>   
>>   	/* for cyclic capability */
>>   	bool cyclic;
>> +
>> +	/* for runtime pm tracking */
>> +	bool active;
>>   };
>>   
>>   struct pl330_dmac {
>> @@ -2031,6 +2034,7 @@ static void pl330_tasklet(unsigned long data)
>>   		_stop(pch->thread);
>>   		spin_unlock(&pch->thread->dmac->lock);
>>   		power_down = true;
>> +		pch->active = false;
>>   	} else {
>>   		/* Make sure the PL330 Channel thread is active */
>>   		spin_lock(&pch->thread->dmac->lock);
>> @@ -2050,6 +2054,7 @@ static void pl330_tasklet(unsigned long data)
>>   			desc->status = PREP;
>>   			list_move_tail(&desc->node, &pch->work_list);
>>   			if (power_down) {
>> +				pch->active = true;
> It's been a while since I was playign with the driver so I don't
> remember everything... but I can't get the logic behind this.
>
> The device is marked as inactive and scheduled to power down. But you
> mark chanel as active.

Please look 3 lines further. The channel is started again (because this
is cyclic request), so setting active to true is justified. Even power_down
is then set to false.

> Maybe it is correct but for me it is unreadable.
>
> I understand that you wanted to mark the device as active at some point,
> in case transfer was terminated?

Frankly, I have no idea how to explain it more clear. Cyclic transfers are
automatically restarted, so the above logic simply keeps pch->active in sync
with hardware operations.

>
> Best regards,
> Krzysztof
>
>>   				spin_lock(&pch->thread->dmac->lock);
>>   				_start(pch->thread);
>>   				spin_unlock(&pch->thread->dmac->lock);
>> @@ -2164,6 +2169,7 @@ static int pl330_terminate_all(struct dma_chan *chan)
>>   	unsigned long flags;
>>   	struct pl330_dmac *pl330 = pch->dmac;
>>   	LIST_HEAD(list);
>> +	bool power_down = false;
>>   
>>   	pm_runtime_get_sync(pl330->ddma.dev);
>>   	spin_lock_irqsave(&pch->lock, flags);
>> @@ -2174,6 +2180,8 @@ static int pl330_terminate_all(struct dma_chan *chan)
>>   	pch->thread->req[0].desc = NULL;
>>   	pch->thread->req[1].desc = NULL;
>>   	pch->thread->req_running = -1;
>> +	power_down = pch->active;
>> +	pch->active = false;
>>   
>>   	/* Mark all desc done */
>>   	list_for_each_entry(desc, &pch->submitted_list, node) {
>> @@ -2191,6 +2199,8 @@ static int pl330_terminate_all(struct dma_chan *chan)
>>   	list_splice_tail_init(&pch->completed_list, &pl330->desc_pool);
>>   	spin_unlock_irqrestore(&pch->lock, flags);
>>   	pm_runtime_mark_last_busy(pl330->ddma.dev);
>> +	if (power_down)
>> +		pm_runtime_put_autosuspend(pl330->ddma.dev);
>>   	pm_runtime_put_autosuspend(pl330->ddma.dev);
>>   
>>   	return 0;
>> @@ -2350,6 +2360,7 @@ static void pl330_issue_pending(struct dma_chan *chan)
>>   		 * updated on work_list emptiness status.
>>   		 */
>>   		WARN_ON(list_empty(&pch->submitted_list));
>> +		pch->active = true;
>>   		pm_runtime_get_sync(pch->dmac->ddma.dev);
>>   	}
>>   	list_splice_tail_init(&pch->submitted_list, &pch->work_list);
>> -- 
>> 1.9.1
>>

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

^ permalink raw reply

* [PATCH] mm: pmd dirty emulation in page fault handler
From: Minchan Kim @ 2016-12-23  9:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161223091725.GA23117@dhcp22.suse.cz>

Hi,

On Fri, Dec 23, 2016 at 10:17:25AM +0100, Michal Hocko wrote:
> On Thu 22-12-16 23:52:03, Minchan Kim wrote:
> [...]
> > >From b3ec95c0df91ad113525968a4a6b53030fd0b48d Mon Sep 17 00:00:00 2001
> > From: Minchan Kim <minchan@kernel.org>
> > Date: Thu, 22 Dec 2016 23:43:49 +0900
> > Subject: [PATCH v2] mm: pmd dirty emulation in page fault handler
> > 
> > Andreas reported [1] made a test in jemalloc hang in THP mode in arm64.
> > http://lkml.kernel.org/r/mvmmvfy37g1.fsf at hawking.suse.de
> > 
> > The problem is page fault handler supports only accessed flag emulation
> > for THP page of SW-dirty/accessed architecture.
> > 
> > This patch enables dirty-bit emulation for those architectures.
> > Without it, MADV_FREE makes application hang by repeated fault forever.
> 
> The changelog is rather terse and considering the issue is rather subtle
> and it aims the stable tree I think it could see more information. How
> do we end up looping in the page fault and why the dirty pmd stops it.
> Could you update the changelog to be more verbose, please? I am still
> digesting this patch but I believe it is correct fwiw...
> 

How about this? Feel free to suggest better wording.

Andreas reported [1] made a test in jemalloc hang in THP mode in arm64.
http://lkml.kernel.org/r/mvmmvfy37g1.fsf at hawking.suse.de

The problem is currently page fault handler doesn't supports dirty bit
emulation of pte for non-HW dirty-bit architecture so that application
stucks until VM marked the pmd dirty.

How the emulation work depends on the architecture. In case of arm64,
when it set up pte firstly, it sets pte PTE_RDONLY to get a chance to
mark the pte dirty via triggering page fault when store access happens.
Once the page fault occurs, VM marks the pte dirty and arch code for
setting pte will clear PTE_RDONLY for application to proceed.

IOW, if VM doesn't mark the pte dirty, application hangs forever by
repeated fault(i.e., store op but the pte is PTE_RDONLY).

This patch enables dirty-bit emulation for those architectures.

^ permalink raw reply

* [PATCH v1] watchdog: imx2: fix hang-up on boot for i.MX21, i.MX27 and i.MX31 SoCs
From: Uwe Kleine-König @ 2016-12-23 10:01 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <74751709-c32e-b1b3-4fa3-613f7d57d1d8@mleia.com>

On Fri, Dec 23, 2016 at 11:27:24AM +0200, Vladimir Zapolskiy wrote:
> On 12/23/2016 10:32 AM, Uwe Kleine-K?nig wrote:
> > Hello,
> > 
> > On Fri, Dec 23, 2016 at 10:20:20AM +0200, Vladimir Zapolskiy wrote:
> >> On 12/23/2016 03:55 AM, Guenter Roeck wrote:
> >>> What is the ultimate conclusion of this exchange ?
> >>>
> >>> Are we going to get another version of the patch, or did everyone agree that
> >>> the patch is good as it is and does not require further changes ?
> >>>
> >>
> >> I can not imagine a different fix.
> > 
> > my preferred fix would be:
> > 
> >  - add an imx35 compatible to all newer dtsi
> >  - update the driver to only write the wmcr on imx35 compatible devices
> >    adding only imx35.
> > 
> 
> It breaks old DTS vs. new kernel compatibility, I've already mentioned this.

Correct, and I didn't deny that. In my mail I pointed out the problem
this imposes and I think it's small enough to not justify the complexity
introduced in your proposed change.

If we cannot agree then at least this needs to be better documented in
the driver because otherwise someone makes a cleanup later dropping all
the compatibles that are needed to keep backwards compatibility.

But my suggestion is to first do the minimal fix, and if in the next
merge window people lament about breakages on their machine, we can
still extend the simple fix to a fully backwards compatible change.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-K?nig            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply

* [PATCH v2 02/10] dt-bindings: hisi: Add Hisilicon HiP05/06/07 Djtag dts bindings
From: Anurup M @ 2016-12-23 10:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161219163126.w6ibkd6ayvblkwqt@rob-hp-laptop>



On Monday 19 December 2016 10:01 PM, Rob Herring wrote:
> On Wed, Dec 07, 2016 at 11:55:19AM -0500, Anurup M wrote:
>> From: Tan Xiaojun <tanxiaojun@huawei.com>
>>
>> Add Hisilicon HiP05/06/07 Djtag dts bindings for CPU and IO Die
>>
>> Signed-off-by: Tan Xiaojun <tanxiaojun@huawei.com>
>> Signed-off-by: Anurup M <anurup.m@huawei.com>
>> ---
>>   .../devicetree/bindings/arm/hisilicon/djtag.txt    | 41 ++++++++++++++++++++++
>>   1 file changed, 41 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/djtag.txt
>>
>> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/djtag.txt b/Documentation/devicetree/bindings/arm/hisilicon/djtag.txt
>> new file mode 100644
>> index 0000000..733498e
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/arm/hisilicon/djtag.txt
>> @@ -0,0 +1,41 @@
>> +The Hisilicon Djtag is an independent component which connects with some other
>> +components in the SoC by Debug Bus. The djtag is available in CPU and IO dies
>> +in the chip. The djtag controls access to connecting modules of CPU and IO
>> +dies.
>> +The various connecting components in CPU die (like L3 cache, L3 cache PMU etc.)
>> +are accessed by djtag during real time debugging. In IO die there are connecting
>> +components like RSA. These components appear as devices atatched to djtag bus.
>> +
>> +Hisilicon HiP05/06 djtag for CPU and HiP05 IO die
>> +Required properties:
>> +  - compatible : "hisilicon,hisi-djtag-v1"
> These need SoC specific compatible strings. They probably should
> also include cpu or io in the compatible string. I would expect there
> are things like events, triggers, or component connections for debug
> that are SoC specifc.

Ok. Shall include SoC prefix in compatible string. e.g. 
"hisilicon,hip06-djtag-v1".
As the djtag driver handling is same for CPU and IO, I think I don't 
need to include
them in the compatible string. Please share your comment.

Thanks,
Anurup

>> +  - reg : Register address and size
>> +  - scl-id : The Super Cluster ID for CPU or IO die
>> +
>> +Hisilicon HiP06 djtag for IO die and HiP07 djtag for CPU and IO die
>> +Required properties:
>> +  - compatible : "hisilicon,hisi-djtag-v2"
> Same here.
>
>> +  - reg : Register address and size
>> +  - scl-id : The Super Cluster ID for CPU or IO die
>> +
>> +Example 1: Djtag for CPU die
>> +
>> +	/* for Hisilicon HiP05 djtag for CPU Die */
>> +	djtag0: djtag at 80010000 {
>> +		compatible = "hisilicon,hisi-djtag-v1";
>> +		reg = <0x0 0x80010000 0x0 0x10000>;
>> +		scl-id = <0x02>;
>> +
>> +		/* All connecting components will appear as child nodes */
>> +	};
>> +
>> +Example 2: Djtag for IO die
>> +
>> +	/* for Hisilicon HiP05 djtag for IO Die */
>> +	djtag1: djtag at d0000000 {
>> +		compatible = "hisilicon,hisi-djtag-v1";
>> +		reg = <0x0 0xd0000000 0x0 0x10000>;
>> +		scl-id = <0x01>;
>> +
>> +		/* All connecting components will appear as child nodes */
>> +	};
>> -- 
>> 2.1.4
>>

^ permalink raw reply

* [PATCH v2 03/10] dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
From: Anurup M @ 2016-12-23 10:23 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161219163702.c7wvqsaa4jcztthh@rob-hp-laptop>



On Monday 19 December 2016 10:07 PM, Rob Herring wrote:
> On Wed, Dec 07, 2016 at 11:55:59AM -0500, Anurup M wrote:
>> 1) Device tree bindings for Hisilicon SoC PMU.
>> 2) Add example for Hisilicon L3 cache and MN PMU.
>> 3) Add child nodes of L3C and MN in djtag bindings example.
>>
>> Signed-off-by: Anurup M <anurup.m@huawei.com>
>> Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
>> ---
>>   .../devicetree/bindings/arm/hisilicon/djtag.txt    | 25 ++++++
>>   .../devicetree/bindings/arm/hisilicon/pmu.txt      | 98 ++++++++++++++++++++++
>>   2 files changed, 123 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
>>
>> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/djtag.txt b/Documentation/devicetree/bindings/arm/hisilicon/djtag.txt
>> index 733498e..c885507 100644
>> --- a/Documentation/devicetree/bindings/arm/hisilicon/djtag.txt
>> +++ b/Documentation/devicetree/bindings/arm/hisilicon/djtag.txt
>> @@ -27,6 +27,31 @@ Example 1: Djtag for CPU die
>>   		scl-id = <0x02>;
>>   
>>   		/* All connecting components will appear as child nodes */
>> +
>> +		pmul3c0 {
>> +			compatible = "hisilicon,hisi-pmu-l3c-v1";
>> +			module-id = <0x04 0x02>;
>> +		};
>> +
>> +		pmul3c1 {
>> +			compatible = "hisilicon,hisi-pmu-l3c-v1";
>> +			module-id = <0x04 0x04>;
>> +		};
>> +
>> +		pmul3c2 {
>> +			compatible = "hisilicon,hisi-pmu-l3c-v1";
>> +			module-id = <0x04 0x01>;
>> +		};
>> +
>> +		pmul3c3 {
>> +			compatible = "hisilicon,hisi-pmu-l3c-v1";
>> +			module-id = <0x04 0x08>;
>> +		};
>> +
>> +		pmumn0 {
>> +			compatible = "hisilicon,hisi-pmu-mn-v1";
>> +			module-id = <0x0b>;
>> +		};
>>   	};
>>   
>>   Example 2: Djtag for IO die
>> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
>> new file mode 100644
>> index 0000000..e2160ad
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
>> @@ -0,0 +1,98 @@
>> +Hisilicon SoC HiP05/06/07 ARMv8 PMU
>> +===================================
>> +
>> +The Hisilicon SoC chips like HiP05/06/07 etc. consist of various independent
>> +system device PMUs such as L3 cache (L3C) and Miscellaneous Nodes(MN). These
>> +PMU devices are independent and have hardware logic to gather statistics and
>> +performance information.
>> +
>> +HiSilicon SoC chip is encapsulated by multiple CPU and IO dies. The CPU die
>> +is called as Super CPU cluster (SCCL) which includes 16 cpu-cores. Every SCCL
>> +in HiP05/06/07 chips are further grouped as CPU clusters (CCL) which includes
>> +4 cpu-cores each.
>> +e.g. In the case of HiP05/06/07, each SCCL has 1 L3 cache and 1 MN PMU device.
>> +The L3 cache is further grouped as 4 L3 cache banks in a SCCL.
>> +
>> +The Hisilicon SoC PMU DT node bindings for uncore PMU devices are as below.
>> +For PMU devices like L3 cache. MN etc. which are accessed using the djtag,
>> +the parent node will be the djtag node of the corresponding CPU die (SCCL).
>> +
>> +L3 cache
>> +---------
>> +The L3 cache is dedicated for each SCCL. Each SCCL in HiP05/06/07 chips have 4
>> +L3 cache banks. Each L3 cache bank have separate DT nodes.
>> +
>> +Required properties:
>> +
>> +	- compatible : This value should be as follows
>> +		(a) "hisilicon,hisi-pmu-l3c-v1" for v1 hw in HiP05/06 chips
>> +		(b) "hisilicon,hisi-pmu-l3c-v2" for v2 hw in HiP07 chip
> Use SoC specific compatible strings.

Ok.

>> +
>> +	- module-id : This property is a combination of two values in the below order.
>> +		      a) Module ID: The module identifier for djtag.
>> +		      b) Instance or Bank ID: This will identify the L3 cache bank
>> +			 or instance.
> Needs a vendor prefix.

Ok. Shall change to "hisi-module-id". Also modify for all vendor 
specific properties.

>> +
>> +Optional properties:
>> +
>> +	- interrupt-parent : A phandle indicating which interrupt controller
>> +		this PMU signals interrupts to.
>> +
>> +	- interrupts : Interrupt lines used by this L3 cache bank.
> How many interrupts and what are they?

A single interrupt. Shall modify as  "Interrupt line used by the L3 
cache bank".

Thanks,
Anurup

>> +
>> +	*The counter overflow IRQ is not supported in v1 hardware (HiP05/06).
>> +
>> +Miscellaneous Node
>> +------------------
>> +The MN is dedicated for each SCCL and hence there are separate DT nodes for MN
>> +for each SCCL.
> Similar comments here.
>
>> +
>> +Required properties:
>> +
>> +	- compatible : This value should be as follows
>> +		(a) "hisilicon,hisi-pmu-mn-v1" for v1 hw in HiP05/06 chips
>> +		(b) "hisilicon,hisi-pmu-mn-v2" for v2 hw in HiP07 chip
>> +
>> +	- module-id : Module ID to input for djtag.
>> +
>> +Optional properties:
>> +
>> +	- interrupt-parent : A phandle indicating which interrupt controller
>> +		this PMU signals interrupts to.
>> +
>> +	- interrupts : Interrupt lines used by this PMU.
>> +
>> +	*The counter overflow IRQ is not supported in v1 hardware (HiP05/06).
>> +
>> +Example:
>> +
>> +	djtag0: djtag at 80010000 {
>> +		compatible = "hisilicon,hisi-djtag-v1";
>> +		reg = <0x0 0x80010000 0x0 0x10000>;
>> +		scl-id = <0x02>;
>> +
>> +		pmul3c0 {
>> +			compatible = "hisilicon,hisi-pmu-l3c-v1";
>> +			module-id = <0x04 0x02>;
>> +		};
>> +
>> +		pmul3c1 {
>> +			compatible = "hisilicon,hisi-pmu-l3c-v1";
>> +			module-id = <0x04 0x04>;
>> +		};
>> +
>> +		pmul3c2 {
>> +			compatible = "hisilicon,hisi-pmu-l3c-v1";
>> +			module-id = <0x04 0x01>;
>> +		};
>> +
>> +		pmul3c3 {
>> +			compatible = "hisilicon,hisi-pmu-l3c-v1";
>> +			module-id = <0x04 0x08>;
>> +		};
>> +
>> +		pmumn0 {
>> +			compatible = "hisilicon,hisi-pmu-mn-v1";
>> +			module-id = <0x0b>;
>> +		};
>> +	};
>> -- 
>> 2.1.4
>>

^ permalink raw reply

* [PATCH v2] i2c: designware: add reset interface
From: Philipp Zabel @ 2016-12-23 10:26 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <49a5d8a4-864e-e80e-eee0-57c876d25aaf@linaro.org>

Hi Zhangfei,

Am Freitag, den 16.12.2016, 11:01 +0800 schrieb zhangfei:
> Hi, Philipp
> 
> On 2016?12?16? 10:45, zhangfei wrote:
[...]
> Sorry, a bit confused.
> Is that mean we should not use devm_reset_control_get_optional()
> While driver should decide whether use 
> devm_reset_control_get_optional_exclusive() or
> devm_reset_control_get_optional_shared()
> What if different platform has different requirement?
> 
> Looks the difference between _exclusive and _shared is refcount,
> How about handle this inside, and not decided by interface?

Because there is a significant difference in how the actual reset line
behaves. The shared resets are clock-like, and should only be used if
the device needs them to be deasserted to be enabled, but is fine if
they have been deasserted earlier or if they are not immediately
asserted after the device is disabled, but some time later.
For the shared / non-exclusive resets calling reset_control_assert and
then reset_control_deassert is not guaranteed to do anything at all,
because another user of the reset line could keep it deasserted.

If the device needs a reset to be issued at a certain point in time on
the other hand, for example to reset its state machine or registers to a
known good state, calling assert must guarantee to actually assert the
reset. This can only be done if the reset is exclusive.

Whether guaranteed asserts (exclusive reset lines) are necessary depends
on the device, so this decision has to be made in the driver.

regards
Philipp

^ permalink raw reply

* [PATCH 2/2] xilinx_dma: Add reset support
From: Philipp Zabel @ 2016-12-23 10:26 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <4539024.0pO4eXcfb0@avalon>

Am Donnerstag, den 15.12.2016, 15:56 +0200 schrieb Laurent Pinchart:
> Hi Ramiro,
> 
> (CC'ing Philipp Zabel)
> 
> On Thursday 15 Dec 2016 11:26:54 Ramiro Oliveira wrote:
> > On 12/14/2016 8:16 PM, Laurent Pinchart wrote:
> > > Hi Ramiro,
> > > 
> > > Thank you for the patch.
> > > 
> > > On Wednesday 14 Dec 2016 17:18:24 Ramiro Oliveira wrote:
> > >> Add a DT property to control an optional external reset line
> > >> 
> > >> Signed-off-by: Ramiro Oliveira <roliveir@synopsys.com>
> > >> ---
> > >> 
> > >>  drivers/dma/xilinx/xilinx_dma.c | 23 +++++++++++++++++++++++
> > >>  1 file changed, 23 insertions(+)
> > >> 
> > >> diff --git a/drivers/dma/xilinx/xilinx_dma.c
> > >> b/drivers/dma/xilinx/xilinx_dma.c index 5c9f11b..b845224 100644
> > >> --- a/drivers/dma/xilinx/xilinx_dma.c
> > >> +++ b/drivers/dma/xilinx/xilinx_dma.c
> > >> @@ -46,6 +46,7 @@
> > >>  #include <linux/slab.h>
> > >>  #include <linux/clk.h>
> > >>  #include <linux/io-64-nonatomic-lo-hi.h>
> > >> +#include <linux/reset.h>
> > > 
> > > I had neatly sorted the header alphabetically until someone added clk.h
> > > and io-64-nonatomic-lo-hi.h :-( Could you please move reset.h just before
> > > slab.h ?
> >
> > Sure. Actually I was tempted to reorder it, but I decided not to do it. I'll
> > do it now
> 
> Yeah, I'll sleep better tonight :-D
> 
> > >>  #include "../dmaengine.h"
> > >> 
> > >> @@ -409,6 +410,7 @@ struct xilinx_dma_device {
> > >>  	struct clk *rxs_clk;
> > >>  	u32 nr_channels;
> > >>  	u32 chan_id;
> > >> +	struct reset_control *rst;
> > >>  };
> > >>  
> > >>  /* Macros */
> > >> @@ -2543,6 +2545,27 @@ static int xilinx_dma_probe(struct platform_device
> > >> *pdev) if (IS_ERR(xdev->regs))
> > >>  		return PTR_ERR(xdev->regs);
> > >> 
> > >> +	xdev->rst = devm_reset_control_get_optional(&pdev->dev, "reset");
> > > 
> > > devm_reset_control_get_optional() is deprecated as explained in
> > > linux/reset.h, you should use devm_reset_control_get_optional_exclusive()
> > > or devm_reset_control_get_optional_shared() instead, as applicable.
> > > 
> > > This being said, I'm wondering why the optional versions of those
> > > functions exist, as they do exactly the same as the non-optional versions.
> > > The API feels wrong, it should have been modelled like the GPIO API. Feel
> > > free to fix it if you want :-) But that's out of scope for this patch.
> > 
> > I missed the comment stating that devm_reset_control_get_optional() was
> > deprecated.
> > 
> > I could fix it. Your sugestion is modelling these functions like the GPIO
> > API?
> 
> I think it would be better for driver if the _get_optional functions would 
> return an ERR_PTR() for errors and NULL when reset control is not available, 
> and then have the rest of the reset controller API accept NULL as a no-op. 
> Your implementation would then be
> 
> 	xdev->rst = devm_reset_control_get_optional_exclusive(&pdev->dev,
> 							      "reset");
> 	if (IS_ERR(xdev->rst)) {
> 		err = PTR_ERR(xdev->rst);
> 		if (err != -EPROBE_DEFER)
> 			dev_err(xdev->dev, "error getting reset %d\n", err);
> 		return err;
> 	}
> 
> 	err = reset_control_deassert(xdev->rst);
> 	if (err) {
> 		dev_err(xdev->dev, "failed to deassert reset: %d\n", err);
> 		return err;
> 	}
> 
> That requires modifying the reset controller API, so it's a bit out-of-scope, 
> but if you could give it a go, it would be great.

Seeing that the clk and gpiod APIs behave in the same way, I think it
would be good to align the reset API with the common behaviour.

regards
Philipp

^ permalink raw reply

* [PATCH 3/3] dmaengine: pl330: Don't require irq-safe runtime PM
From: Marek Szyprowski @ 2016-12-23 10:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161223024832.GA4178@kozik-lap>

Hi Krzysztof,


On 2016-12-23 03:48, Krzysztof Kozlowski wrote:
> On Thu, Dec 22, 2016 at 01:11:29PM +0100, Marek Szyprowski wrote:
>> This patch replaces irq-safe runtime pm with non-irq-safe version.
>>
>> Till now non-irq-safe runtime PM implementation was only possible by calling
>> pm_runtime_get/put functions from alloc/free_chan_resources. All other DMA
>> engine API functions cannot be called from a context, which allows sleeping.
>> Such implementation in practice resulted in keeping PL330 DMA controller
>> device active almost all the time, because most of the slave device drivers
>> (DMA engine clients) acquired DMA channel in their probe() function and
>> released it during driver removal.
> I think that is not accurate (unless something changed recently and I
> missed it)... The runtime PM in pl330 was infact suspending device when
> the queue was empty. It was working during the regular work of DMA and
> slave devices.
>
> Maybe your recent fix changed this?

Please note that the above description was about existing ways of 
implementing
non-irq-safe runtime pm in a dmaengine driver. PL330 was using irq-safe 
runtime pm,
which has other issues - the most significant one is keeping clocks 
prepared all
the time and keeping power domain turned on all the time. Both are real 
blockers
of taking advantages of the fact that there is a separate power domain 
for audio
hardware.

>> This patch provides a new approach.
> Please say why this approach is better because the previous one - was
> suspending the dma channel when not used. Otherwise, it would make no
> sense of implement the irq-safe runtime PM at the beginning!
>
> Of course, a change from irq-safe to non-irq-safe is a good reason to
> implement changes. But that was not mentioned in the first paragraph at
> all.

The goal of this patchset is to add audio power domain support for 
Exynos5 SoCs
and let it to be turned off when no sound is being played/recorded. This 
in turn
requires all devices, which are a part of that power domain to implement 
runtime
pm support. Maybe I didn't state this strong enough, but I think that 
there was
such information in the commit message AND cover letter.

>> The main assumption for it is an
>> observation that there can be only one slave device using each DMA channel.
> Wait, observation, real requirement or assumption?
>
> Later in the code I see adding such requirement.

Well, observation which result in assumption. I cannot imagine a 
hardware which
shares slave DMA channel between devices. Also none of the existing platform
does it.

>> Using recently introduced device dependencies (links) infrastructure one can
>> ensure proper runtime PM state of PL330 DMA controller. In this approach in
>> pl330_alloc_chan_resources() function a new dependency is being created
>> between PL330 DMA controller device (as supplier) and given slave device
>> (as consumer). This way PL330 DMA controller device runtime active counter
>> is increased when the slave device is resumed and decreased the same time
>> when given slave device is put to suspend. This way it has been ensured to
>> keep PL330 DMA controller runtime active if there is an active used of any
>> of its DMA channels. Slave device pointer is initially stored in per-channel
>> data in of_dma_xlate callback. This is similar to what has been already
>> implemented in Exynos IOMMU driver in commit 2f5f44f205cc958b
>> ("iommu/exynos: Use device dependency links to control runtime pm").
> Sounds convincing... Interesting approach!
>
> My doubts are:
> 1. What with more then one slave device? (assumption?)

See above, there are no such cases.

> 2. If slave device does not implement runtime PM, then pl330 will be
>     active all the time?

Right, but the goal is to have runtime pm added to all devices in the 
system.

> 3. If slave device implements runtime PM in a way that it's enabled in
>     probe and released in remove, then pl330 will be active all the time?

Then it will force power domain to be turned on all the time and even 
optional
fine-grained irq-safe runtiem pm in pl330 driver won't help much to 
reduce power
consumption. I assume that the real goal with runtime pm is to let 
respective
power domains to be turned off, what gives the best results in terms of 
power
saving.

>> If one requests memory-to-memory chanel, runtime active counter is
>> increased unconditionally. This might be a drawback of this approach, but
>> PL330 is not really used for memory-to-memory operations due to poor
>> performance in such operations compared to CPU.
>>
>> Introducing non-irq-safe runtime power management finally allows to turn
>> audio power domain off on Exynos5 SoCs.
> ... and actually any domain which contained pl330 device so this is nice
> benefit!
>
>> Removal of irq-safe runtime pm is based on the revert of the following
>> commits:
>> 1. "dmaengine: pl330: fix runtime pm support" commit <no stable id yet>
>> 2. "dmaengine: pl330: Fix hang on dmaengine_terminate_all on certain boards"
>>     commit 81cc6edc08705ac0146fe6ac14a0982a31ce6f3d
>> 3. "ARM: 8202/1: dmaengine: pl330: Add runtime Power Management support v12"
>>     commit ae43b3289186480f81c78bb63d788a85a3631f47
>>
>> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
>> ---
>>   drivers/dma/pl330.c | 133 +++++++++++++++++++++++++++-------------------------
>>   1 file changed, 70 insertions(+), 63 deletions(-)
>>
>> diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
>> index 3c80e71..2cffbb4 100644
>> --- a/drivers/dma/pl330.c
>> +++ b/drivers/dma/pl330.c
>> @@ -268,9 +268,6 @@ enum pl330_byteswap {
>>   
>>   #define NR_DEFAULT_DESC	16
>>   
>> -/* Delay for runtime PM autosuspend, ms */
>> -#define PL330_AUTOSUSPEND_DELAY 20
>> -
>>   /* Populated by the PL330 core driver for DMA API driver's info */
>>   struct pl330_config {
>>   	u32	periph_id;
>> @@ -449,7 +446,8 @@ struct dma_pl330_chan {
>>   	bool cyclic;
>>   
>>   	/* for runtime pm tracking */
>> -	bool active;
>> +	struct device *slave;
>> +	struct device_link *slave_link;
>>   };
>>   
>>   struct pl330_dmac {
>> @@ -2015,7 +2013,6 @@ static void pl330_tasklet(unsigned long data)
>>   	struct dma_pl330_chan *pch = (struct dma_pl330_chan *)data;
>>   	struct dma_pl330_desc *desc, *_dt;
>>   	unsigned long flags;
>> -	bool power_down = false;
>>   
>>   	spin_lock_irqsave(&pch->lock, flags);
>>   
>> @@ -2030,18 +2027,10 @@ static void pl330_tasklet(unsigned long data)
>>   	/* Try to submit a req imm. next to the last completed cookie */
>>   	fill_queue(pch);
>>   
>> -	if (list_empty(&pch->work_list)) {
>> -		spin_lock(&pch->thread->dmac->lock);
>> -		_stop(pch->thread);
>> -		spin_unlock(&pch->thread->dmac->lock);
>> -		power_down = true;
>> -		pch->active = false;
>> -	} else {
>> -		/* Make sure the PL330 Channel thread is active */
>> -		spin_lock(&pch->thread->dmac->lock);
>> -		_start(pch->thread);
>> -		spin_unlock(&pch->thread->dmac->lock);
>> -	}
>> +	/* Make sure the PL330 Channel thread is active */
>> +	spin_lock(&pch->thread->dmac->lock);
>> +	_start(pch->thread);
>> +	spin_unlock(&pch->thread->dmac->lock);
>>   
>>   	while (!list_empty(&pch->completed_list)) {
>>   		struct dmaengine_desc_callback cb;
>> @@ -2054,13 +2043,6 @@ static void pl330_tasklet(unsigned long data)
>>   		if (pch->cyclic) {
>>   			desc->status = PREP;
>>   			list_move_tail(&desc->node, &pch->work_list);
>> -			if (power_down) {
>> -				pch->active = true;
>> -				spin_lock(&pch->thread->dmac->lock);
>> -				_start(pch->thread);
>> -				spin_unlock(&pch->thread->dmac->lock);
>> -				power_down = false;
>> -			}
>>   		} else {
>>   			desc->status = FREE;
>>   			list_move_tail(&desc->node, &pch->dmac->desc_pool);
>> @@ -2075,12 +2057,6 @@ static void pl330_tasklet(unsigned long data)
>>   		}
>>   	}
>>   	spin_unlock_irqrestore(&pch->lock, flags);
>> -
>> -	/* If work list empty, power down */
>> -	if (power_down) {
>> -		pm_runtime_mark_last_busy(pch->dmac->ddma.dev);
>> -		pm_runtime_put_autosuspend(pch->dmac->ddma.dev);
>> -	}
>>   }
>>   
>>   bool pl330_filter(struct dma_chan *chan, void *param)
>> @@ -2113,14 +2089,63 @@ static struct dma_chan *of_dma_pl330_xlate(struct of_phandle_args *dma_spec,
>>   	if (chan_id >= pl330->num_peripherals)
>>   		return NULL;
>>   
>> +	if (!pl330->peripherals[chan_id].slave)
>> +		pl330->peripherals[chan_id].slave = slave;
>> +	else if (pl330->peripherals[chan_id].slave != slave) {
>> +		dev_err(pl330->ddma.dev,
>> +			"Can't use same channel with multiple slave devices!\n");
>> +		return NULL;
>> +	}
> This could be nicely split into separate patch.

Okay, if you want, I can move this change to separate patch.

> Best regards,
> Krzysztof
>
>> +
>>   	return dma_get_slave_channel(&pl330->peripherals[chan_id].chan);
>>   }
>>   
>> +static int pl330_add_slave_link(struct pl330_dmac *pl330,
>> +				struct dma_pl330_chan *pch)
>> +{
>> +	struct device_link *link;
>> +	int i;
>> +
>> +	if (pch->slave_link)
>> +		return 0;
>> +
>> +	link = device_link_add(pch->slave, pl330->ddma.dev,
>> +				       DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE);
>> +	if (!link)
>> +		return -ENODEV;
>> +
>> +	for (i = 0; i < pl330->num_peripherals; i++)
>> +		if (pl330->peripherals[i].slave == pch->slave)
>> +			pl330->peripherals[i].slave_link = link;
>> +	return 0;
>> +}
>> +
>> +static void pl330_del_slave_link(struct pl330_dmac *pl330,
>> +				 struct dma_pl330_chan *pch)
>> +{
>> +	struct device_link *link = pch->slave_link;
>> +	int i, count = 0;
>> +
>> +	for (i = 0; i < pl330->num_peripherals; i++)
>> +		if (pl330->peripherals[i].slave == pch->slave &&
>> +		    pl330->peripherals[i].thread)
>> +			count++;
>> +
>> +	if (count > 0)
>> +		return;
>> +
>> +	device_link_del(link);
>> +	for (i = 0; i < pl330->num_peripherals; i++)
>> +		if (pl330->peripherals[i].slave == pch->slave)
>> +			pch->slave_link = NULL;
>> +}
>> +
>>   static int pl330_alloc_chan_resources(struct dma_chan *chan)
>>   {
>>   	struct dma_pl330_chan *pch = to_pchan(chan);
>>   	struct pl330_dmac *pl330 = pch->dmac;
>>   	unsigned long flags;
>> +	int ret = 0;
>>   
>>   	spin_lock_irqsave(&pch->lock, flags);
>>   
>> @@ -2137,6 +2162,14 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan)
>>   
>>   	spin_unlock_irqrestore(&pch->lock, flags);
>>   
>> +	if (pch->slave)
>> +		ret = pl330_add_slave_link(pl330, pch);
>> +	else
>> +		ret = pm_runtime_get_sync(pl330->ddma.dev);
>> +
>> +	if (ret < 0)
>> +		return ret;
>> +
>>   	return 1;
>>   }
>>   
>> @@ -2171,9 +2204,7 @@ static int pl330_terminate_all(struct dma_chan *chan)
>>   	unsigned long flags;
>>   	struct pl330_dmac *pl330 = pch->dmac;
>>   	LIST_HEAD(list);
>> -	bool power_down = false;
>>   
>> -	pm_runtime_get_sync(pl330->ddma.dev);
>>   	spin_lock_irqsave(&pch->lock, flags);
>>   	spin_lock(&pl330->lock);
>>   	_stop(pch->thread);
>> @@ -2182,8 +2213,6 @@ static int pl330_terminate_all(struct dma_chan *chan)
>>   	pch->thread->req[0].desc = NULL;
>>   	pch->thread->req[1].desc = NULL;
>>   	pch->thread->req_running = -1;
>> -	power_down = pch->active;
>> -	pch->active = false;
>>   
>>   	/* Mark all desc done */
>>   	list_for_each_entry(desc, &pch->submitted_list, node) {
>> @@ -2200,10 +2229,6 @@ static int pl330_terminate_all(struct dma_chan *chan)
>>   	list_splice_tail_init(&pch->work_list, &pl330->desc_pool);
>>   	list_splice_tail_init(&pch->completed_list, &pl330->desc_pool);
>>   	spin_unlock_irqrestore(&pch->lock, flags);
>> -	pm_runtime_mark_last_busy(pl330->ddma.dev);
>> -	if (power_down)
>> -		pm_runtime_put_autosuspend(pl330->ddma.dev);
>> -	pm_runtime_put_autosuspend(pl330->ddma.dev);
>>   
>>   	return 0;
>>   }
>> @@ -2221,7 +2246,6 @@ static int pl330_pause(struct dma_chan *chan)
>>   	struct pl330_dmac *pl330 = pch->dmac;
>>   	unsigned long flags;
>>   
>> -	pm_runtime_get_sync(pl330->ddma.dev);
>>   	spin_lock_irqsave(&pch->lock, flags);
>>   
>>   	spin_lock(&pl330->lock);
>> @@ -2229,8 +2253,6 @@ static int pl330_pause(struct dma_chan *chan)
>>   	spin_unlock(&pl330->lock);
>>   
>>   	spin_unlock_irqrestore(&pch->lock, flags);
>> -	pm_runtime_mark_last_busy(pl330->ddma.dev);
>> -	pm_runtime_put_autosuspend(pl330->ddma.dev);
>>   
>>   	return 0;
>>   }
>> @@ -2238,11 +2260,11 @@ static int pl330_pause(struct dma_chan *chan)
>>   static void pl330_free_chan_resources(struct dma_chan *chan)
>>   {
>>   	struct dma_pl330_chan *pch = to_pchan(chan);
>> +	struct pl330_dmac *pl330 = pch->dmac;
>>   	unsigned long flags;
>>   
>>   	tasklet_kill(&pch->task);
>>   
>> -	pm_runtime_get_sync(pch->dmac->ddma.dev);
>>   	spin_lock_irqsave(&pch->lock, flags);
>>   
>>   	pl330_release_channel(pch->thread);
>> @@ -2252,19 +2274,20 @@ static void pl330_free_chan_resources(struct dma_chan *chan)
>>   		list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool);
>>   
>>   	spin_unlock_irqrestore(&pch->lock, flags);
>> -	pm_runtime_mark_last_busy(pch->dmac->ddma.dev);
>> -	pm_runtime_put_autosuspend(pch->dmac->ddma.dev);
>> +
>> +	if (pch->slave)
>> +		pl330_del_slave_link(pl330, pch);
>> +	else
>> +		pm_runtime_put(pl330->ddma.dev);
>>   }
>>   
>>   static int pl330_get_current_xferred_count(struct dma_pl330_chan *pch,
>>   					   struct dma_pl330_desc *desc)
>>   {
>>   	struct pl330_thread *thrd = pch->thread;
>> -	struct pl330_dmac *pl330 = pch->dmac;
>>   	void __iomem *regs = thrd->dmac->base;
>>   	u32 val, addr;
>>   
>> -	pm_runtime_get_sync(pl330->ddma.dev);
>>   	val = addr = 0;
>>   	if (desc->rqcfg.src_inc) {
>>   		val = readl(regs + SA(thrd->id));
>> @@ -2273,8 +2296,6 @@ static int pl330_get_current_xferred_count(struct dma_pl330_chan *pch,
>>   		val = readl(regs + DA(thrd->id));
>>   		addr = desc->px.dst_addr;
>>   	}
>> -	pm_runtime_mark_last_busy(pch->dmac->ddma.dev);
>> -	pm_runtime_put_autosuspend(pl330->ddma.dev);
>>   
>>   	/* If DMAMOV hasn't finished yet, SAR/DAR can be zero */
>>   	if (!val)
>> @@ -2360,16 +2381,6 @@ static void pl330_issue_pending(struct dma_chan *chan)
>>   	unsigned long flags;
>>   
>>   	spin_lock_irqsave(&pch->lock, flags);
>> -	if (list_empty(&pch->work_list)) {
>> -		/*
>> -		 * Warn on nothing pending. Empty submitted_list may
>> -		 * break our pm_runtime usage counter as it is
>> -		 * updated on work_list emptiness status.
>> -		 */
>> -		WARN_ON(list_empty(&pch->submitted_list));
>> -		pch->active = true;
>> -		pm_runtime_get_sync(pch->dmac->ddma.dev);
>> -	}
>>   	list_splice_tail_init(&pch->submitted_list, &pch->work_list);
>>   	spin_unlock_irqrestore(&pch->lock, flags);
>>   
>> @@ -2987,11 +2998,7 @@ static int __maybe_unused pl330_resume(struct device *dev)
>>   		pcfg->data_buf_dep, pcfg->data_bus_width / 8, pcfg->num_chan,
>>   		pcfg->num_peri, pcfg->num_events);
>>   
>> -	pm_runtime_irq_safe(&adev->dev);
>> -	pm_runtime_use_autosuspend(&adev->dev);
>> -	pm_runtime_set_autosuspend_delay(&adev->dev, PL330_AUTOSUSPEND_DELAY);
>> -	pm_runtime_mark_last_busy(&adev->dev);
>> -	pm_runtime_put_autosuspend(&adev->dev);
>> +	pm_runtime_put(&adev->dev);
>>   
>>   	return 0;
>>   probe_err3:
>> -- 
>> 1.9.1
>>
>
>

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

^ permalink raw reply

* [PATCH] PCI: exynos: refactor exynos pcie driver
From: Pankaj Dubey @ 2016-12-23 10:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CGME20161223105411epcas1p3727f726e757ec6c6d7bff04a9af40077@epcas1p3.samsung.com>

From: Niyas Ahmed S T <niyas.ahmed@samsung.com>

Currently Exynos PCIe driver is only supported for Exynos5440 SoC.
This patch does refactoring of Exynos PCIe driver to extend support
for other Exynos SoC.

Following are the main changes done via this patch:
1) It adds separate structs for memory, clock resources.
2) It add exynos_pcie_ops struct which will allow us to support the
differences in resources in different Exynos SoC.

No functional change intended.

Signed-off-by: Niyas Ahmed S T <niyas.ahmed@samsung.com>
Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
---
This patch set is prepared on top of Krzysztof's for-next and
PCIe driver cleanup patch [1] by Jaehoon Chung.

[1]: https://lkml.org/lkml/2016/12/19/44


 drivers/pci/host/pci-exynos.c | 346 ++++++++++++++++++++++++++----------------
 1 file changed, 217 insertions(+), 129 deletions(-)

diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
index 33562cf..2dc54f7 100644
--- a/drivers/pci/host/pci-exynos.c
+++ b/drivers/pci/host/pci-exynos.c
@@ -17,6 +17,7 @@
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
+#include <linux/of_device.h>
 #include <linux/of_gpio.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
@@ -28,16 +29,6 @@
 
 #define to_exynos_pcie(x)	container_of(x, struct exynos_pcie, pp)
 
-struct exynos_pcie {
-	struct pcie_port	pp;
-	void __iomem		*elbi_base;	/* DT 0th resource */
-	void __iomem		*phy_base;	/* DT 1st resource */
-	void __iomem		*block_base;	/* DT 2nd resource */
-	int			reset_gpio;
-	struct clk		*clk;
-	struct clk		*bus_clk;
-};
-
 /* PCIe ELBI registers */
 #define PCIE_IRQ_PULSE			0x000
 #define IRQ_INTA_ASSERT			BIT(0)
@@ -102,6 +93,122 @@ struct exynos_pcie {
 #define PCIE_PHY_TRSV3_PD_TSV		BIT(7)
 #define PCIE_PHY_TRSV3_LVCC		0x31c
 
+struct exynos_pcie_mem_res {
+	void __iomem *elbi_base; /* DT 0th resource: PCIE CTRL */
+	void __iomem *phy_base; /* DT 1st resource: PHY CTRL */
+	void __iomem *block_base; /* DT 2nd resource: PHY ADDITIONAL CTRL */
+};
+
+struct exynos_pcie_clk_res {
+	struct clk *clk;
+	struct clk *bus_clk;
+};
+
+struct exynos_pcie {
+	struct pcie_port		pp;
+	struct exynos_pcie_mem_res	*mem_res;
+	struct exynos_pcie_clk_res	*clk_res;
+	const struct exynos_pcie_ops	*ops;
+	int				reset_gpio;
+};
+
+struct exynos_pcie_ops {
+	int (*get_mem_resources)(struct platform_device *pdev,
+			struct exynos_pcie *ep);
+	int (*get_clk_resources)(struct exynos_pcie *ep);
+	int (*init_clk_resources)(struct exynos_pcie *ep);
+	void (*deinit_clk_resources)(struct exynos_pcie *ep);
+};
+
+static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev,
+						struct exynos_pcie *ep)
+{
+	struct resource *res;
+	struct device *dev = ep->pp.dev;
+
+	ep->mem_res = devm_kzalloc(dev, sizeof(*ep->mem_res), GFP_KERNEL);
+	if (!ep->mem_res)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ep->mem_res->elbi_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(ep->mem_res->elbi_base))
+		return PTR_ERR(ep->mem_res->elbi_base);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	ep->mem_res->phy_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(ep->mem_res->phy_base))
+		return PTR_ERR(ep->mem_res->phy_base);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+	ep->mem_res->block_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(ep->mem_res->block_base))
+		return PTR_ERR(ep->mem_res->block_base);
+
+	return 0;
+}
+
+static int exynos5440_pcie_get_clk_resources(struct exynos_pcie *ep)
+{
+	struct device *dev = ep->pp.dev;
+
+	ep->clk_res = devm_kzalloc(dev, sizeof(*ep->clk_res), GFP_KERNEL);
+	if (!ep->clk_res)
+		return -ENOMEM;
+
+	ep->clk_res->clk = devm_clk_get(dev, "pcie");
+	if (IS_ERR(ep->clk_res->clk)) {
+		dev_err(dev, "Failed to get pcie rc clock\n");
+		return PTR_ERR(ep->clk_res->clk);
+	}
+
+	ep->clk_res->bus_clk = devm_clk_get(dev, "pcie_bus");
+	if (IS_ERR(ep->clk_res->bus_clk)) {
+		dev_err(dev, "Failed to get pcie bus clock\n");
+		return PTR_ERR(ep->clk_res->bus_clk);
+	}
+
+	return 0;
+}
+
+static int exynos5440_pcie_init_clk_resources(struct exynos_pcie *ep)
+{
+	struct device *dev = ep->pp.dev;
+	int ret;
+
+	ret = clk_prepare_enable(ep->clk_res->clk);
+	if (ret) {
+		dev_err(dev, "cannot enable pcie rc clock");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(ep->clk_res->bus_clk);
+	if (ret) {
+		dev_err(dev, "cannot enable pcie bus clock");
+		goto err_bus_clk;
+	}
+
+	return 0;
+
+err_bus_clk:
+	clk_disable_unprepare(ep->clk_res->clk);
+
+	return ret;
+}
+
+static void exynos5440_pcie_deinit_clk_resources(struct exynos_pcie *ep)
+{
+	clk_disable_unprepare(ep->clk_res->bus_clk);
+	clk_disable_unprepare(ep->clk_res->clk);
+}
+
+static const struct exynos_pcie_ops exynos5440_pcie_ops = {
+	.get_mem_resources	= exynos5440_pcie_get_mem_resources,
+	.get_clk_resources	= exynos5440_pcie_get_clk_resources,
+	.init_clk_resources	= exynos5440_pcie_init_clk_resources,
+	.deinit_clk_resources	= exynos5440_pcie_deinit_clk_resources,
+};
+
 static void exynos_pcie_writel(void __iomem *base, u32 val, u32 reg)
 {
 	writel(val, base + reg);
@@ -116,155 +223,155 @@ static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *ep, bool on)
 {
 	u32 val;
 
-	val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_AWMISC);
+	val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_AWMISC);
 	if (on)
 		val |= PCIE_ELBI_SLV_DBI_ENABLE;
 	else
 		val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
-	exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
+	exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
 }
 
 static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *ep, bool on)
 {
 	u32 val;
 
-	val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_ARMISC);
+	val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_ELBI_SLV_ARMISC);
 	if (on)
 		val |= PCIE_ELBI_SLV_DBI_ENABLE;
 	else
 		val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
-	exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
+	exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
 }
 
 static void exynos_pcie_assert_core_reset(struct exynos_pcie *ep)
 {
 	u32 val;
 
-	val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
+	val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
 	val &= ~PCIE_CORE_RESET_ENABLE;
-	exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
-	exynos_pcie_writel(ep->elbi_base, 0, PCIE_PWR_RESET);
-	exynos_pcie_writel(ep->elbi_base, 0, PCIE_STICKY_RESET);
-	exynos_pcie_writel(ep->elbi_base, 0, PCIE_NONSTICKY_RESET);
+	exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
+	exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_PWR_RESET);
+	exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_STICKY_RESET);
+	exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_NONSTICKY_RESET);
 }
 
 static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep)
 {
 	u32 val;
 
-	val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
+	val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_CORE_RESET);
 	val |= PCIE_CORE_RESET_ENABLE;
 
-	exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
-	exynos_pcie_writel(ep->elbi_base, 1, PCIE_STICKY_RESET);
-	exynos_pcie_writel(ep->elbi_base, 1, PCIE_NONSTICKY_RESET);
-	exynos_pcie_writel(ep->elbi_base, 1, PCIE_APP_INIT_RESET);
-	exynos_pcie_writel(ep->elbi_base, 0, PCIE_APP_INIT_RESET);
-	exynos_pcie_writel(ep->block_base, 1, PCIE_PHY_MAC_RESET);
+	exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_CORE_RESET);
+	exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_STICKY_RESET);
+	exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_NONSTICKY_RESET);
+	exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_APP_INIT_RESET);
+	exynos_pcie_writel(ep->mem_res->elbi_base, 0, PCIE_APP_INIT_RESET);
+	exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_MAC_RESET);
 }
 
 static void exynos_pcie_assert_phy_reset(struct exynos_pcie *ep)
 {
-	exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_MAC_RESET);
-	exynos_pcie_writel(ep->block_base, 1, PCIE_PHY_GLOBAL_RESET);
+	exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_MAC_RESET);
+	exynos_pcie_writel(ep->mem_res->block_base, 1, PCIE_PHY_GLOBAL_RESET);
 }
 
 static void exynos_pcie_deassert_phy_reset(struct exynos_pcie *ep)
 {
-	exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_GLOBAL_RESET);
-	exynos_pcie_writel(ep->elbi_base, 1, PCIE_PWR_RESET);
-	exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_COMMON_RESET);
-	exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_CMN_REG);
-	exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_TRSVREG_RESET);
-	exynos_pcie_writel(ep->block_base, 0, PCIE_PHY_TRSV_RESET);
+	exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_GLOBAL_RESET);
+	exynos_pcie_writel(ep->mem_res->elbi_base, 1, PCIE_PWR_RESET);
+	exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_COMMON_RESET);
+	exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_CMN_REG);
+	exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSVREG_RESET);
+	exynos_pcie_writel(ep->mem_res->block_base, 0, PCIE_PHY_TRSV_RESET);
 }
 
 static void exynos_pcie_power_on_phy(struct exynos_pcie *ep)
 {
 	u32 val;
 
-	val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
+	val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER);
 	val &= ~PCIE_PHY_COMMON_PD_CMN;
-	exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
+	exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER);
 
-	val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
+	val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER);
 	val &= ~PCIE_PHY_TRSV0_PD_TSV;
-	exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
+	exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER);
 
-	val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
+	val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER);
 	val &= ~PCIE_PHY_TRSV1_PD_TSV;
-	exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
+	exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER);
 
-	val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
+	val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER);
 	val &= ~PCIE_PHY_TRSV2_PD_TSV;
-	exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
+	exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER);
 
-	val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
+	val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER);
 	val &= ~PCIE_PHY_TRSV3_PD_TSV;
-	exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
+	exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER);
 }
 
 static void exynos_pcie_power_off_phy(struct exynos_pcie *ep)
 {
 	u32 val;
 
-	val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_COMMON_POWER);
+	val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_COMMON_POWER);
 	val |= PCIE_PHY_COMMON_PD_CMN;
-	exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER);
+	exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_COMMON_POWER);
 
-	val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER);
+	val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV0_POWER);
 	val |= PCIE_PHY_TRSV0_PD_TSV;
-	exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER);
+	exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV0_POWER);
 
-	val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER);
+	val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV1_POWER);
 	val |= PCIE_PHY_TRSV1_PD_TSV;
-	exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER);
+	exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV1_POWER);
 
-	val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER);
+	val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV2_POWER);
 	val |= PCIE_PHY_TRSV2_PD_TSV;
-	exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER);
+	exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV2_POWER);
 
-	val = exynos_pcie_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER);
+	val = exynos_pcie_readl(ep->mem_res->phy_base, PCIE_PHY_TRSV3_POWER);
 	val |= PCIE_PHY_TRSV3_PD_TSV;
-	exynos_pcie_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER);
+	exynos_pcie_writel(ep->mem_res->phy_base, val, PCIE_PHY_TRSV3_POWER);
 }
 
 static void exynos_pcie_init_phy(struct exynos_pcie *ep)
 {
 	/* DCC feedback control off */
-	exynos_pcie_writel(ep->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
+	exynos_pcie_writel(ep->mem_res->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK);
 
 	/* set TX/RX impedance */
-	exynos_pcie_writel(ep->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
+	exynos_pcie_writel(ep->mem_res->phy_base, 0xd5, PCIE_PHY_IMPEDANCE);
 
 	/* set 50Mhz PHY clock */
-	exynos_pcie_writel(ep->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
-	exynos_pcie_writel(ep->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
+	exynos_pcie_writel(ep->mem_res->phy_base, 0x14, PCIE_PHY_PLL_DIV_0);
+	exynos_pcie_writel(ep->mem_res->phy_base, 0x12, PCIE_PHY_PLL_DIV_1);
 
 	/* set TX Differential output for lane 0 */
-	exynos_pcie_writel(ep->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
+	exynos_pcie_writel(ep->mem_res->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL);
 
 	/* set TX Pre-emphasis Level Control for lane 0 to minimum */
-	exynos_pcie_writel(ep->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
+	exynos_pcie_writel(ep->mem_res->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL);
 
 	/* set RX clock and data recovery bandwidth */
-	exynos_pcie_writel(ep->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
-	exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
-	exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
-	exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
-	exynos_pcie_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
+	exynos_pcie_writel(ep->mem_res->phy_base, 0xe7, PCIE_PHY_PLL_BIAS);
+	exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR);
+	exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR);
+	exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR);
+	exynos_pcie_writel(ep->mem_res->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR);
 
 	/* change TX Pre-emphasis Level Control for lanes */
-	exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
-	exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
-	exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
-	exynos_pcie_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
+	exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL);
+	exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL);
+	exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL);
+	exynos_pcie_writel(ep->mem_res->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL);
 
 	/* set LVCC */
-	exynos_pcie_writel(ep->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
-	exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
-	exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
-	exynos_pcie_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
+	exynos_pcie_writel(ep->mem_res->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC);
+	exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC);
+	exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC);
+	exynos_pcie_writel(ep->mem_res->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC);
 }
 
 static void exynos_pcie_assert_reset(struct exynos_pcie *exynos_pcie)
@@ -295,25 +402,27 @@ static int exynos_pcie_establish_link(struct exynos_pcie *exynos_pcie)
 	exynos_pcie_init_phy(exynos_pcie);
 
 	/* pulse for common reset */
-	exynos_pcie_writel(exynos_pcie->block_base, 1, PCIE_PHY_COMMON_RESET);
+	exynos_pcie_writel(exynos_pcie->mem_res->block_base, 1,
+				PCIE_PHY_COMMON_RESET);
 	udelay(500);
-	exynos_pcie_writel(exynos_pcie->block_base, 0, PCIE_PHY_COMMON_RESET);
+	exynos_pcie_writel(exynos_pcie->mem_res->block_base, 0,
+				PCIE_PHY_COMMON_RESET);
 
 	exynos_pcie_deassert_core_reset(exynos_pcie);
 	dw_pcie_setup_rc(pp);
 	exynos_pcie_assert_reset(exynos_pcie);
 
 	/* assert LTSSM enable */
-	exynos_pcie_writel(exynos_pcie->elbi_base, PCIE_ELBI_LTSSM_ENABLE,
-			  PCIE_APP_LTSSM_ENABLE);
+	exynos_pcie_writel(exynos_pcie->mem_res->elbi_base,
+			PCIE_ELBI_LTSSM_ENABLE, PCIE_APP_LTSSM_ENABLE);
 
 	/* check if the link is up or not */
 	if (!dw_pcie_wait_for_link(pp))
 		return 0;
 
-	while (exynos_pcie_readl(exynos_pcie->phy_base,
+	while (exynos_pcie_readl(exynos_pcie->mem_res->phy_base,
 				PCIE_PHY_PLL_LOCKED) == 0) {
-		val = exynos_pcie_readl(exynos_pcie->block_base,
+		val = exynos_pcie_readl(exynos_pcie->mem_res->block_base,
 				PCIE_PHY_PLL_LOCKED);
 		dev_info(dev, "PLL Locked: 0x%x\n", val);
 	}
@@ -325,8 +434,8 @@ static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *ep)
 {
 	u32 val;
 
-	val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_PULSE);
-	exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_PULSE);
+	val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_PULSE);
+	exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_PULSE);
 }
 
 static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
@@ -336,7 +445,7 @@ static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
 	/* enable INTX interrupt */
 	val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
 		IRQ_INTC_ASSERT | IRQ_INTD_ASSERT;
-	exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_PULSE);
+	exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_PULSE);
 }
 
 static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
@@ -363,9 +472,9 @@ static void exynos_pcie_msi_init(struct exynos_pcie *ep)
 	dw_pcie_msi_init(pp);
 
 	/* enable MSI interrupt */
-	val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_EN_LEVEL);
+	val = exynos_pcie_readl(ep->mem_res->elbi_base, PCIE_IRQ_EN_LEVEL);
 	val |= IRQ_MSI_ENABLE;
-	exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_LEVEL);
+	exynos_pcie_writel(ep->mem_res->elbi_base, val, PCIE_IRQ_EN_LEVEL);
 }
 
 static void exynos_pcie_enable_interrupts(struct exynos_pcie *exynos_pcie)
@@ -425,7 +534,8 @@ static int exynos_pcie_link_up(struct pcie_port *pp)
 	struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 	u32 val;
 
-	val = exynos_pcie_readl(exynos_pcie->elbi_base, PCIE_ELBI_RDLH_LINKUP);
+	val = exynos_pcie_readl(exynos_pcie->mem_res->elbi_base,
+					PCIE_ELBI_RDLH_LINKUP);
 	if (val == PCIE_ELBI_LTSSM_ENABLE)
 		return 1;
 
@@ -503,7 +613,6 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
 	struct exynos_pcie *exynos_pcie;
 	struct pcie_port *pp;
 	struct device_node *np = dev->of_node;
-	struct resource *res;
 	int ret;
 
 	exynos_pcie = devm_kzalloc(dev, sizeof(*exynos_pcie), GFP_KERNEL);
@@ -513,59 +622,37 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
 	pp = &exynos_pcie->pp;
 	pp->dev = dev;
 
-	exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
-
-	exynos_pcie->clk = devm_clk_get(dev, "pcie");
-	if (IS_ERR(exynos_pcie->clk)) {
-		dev_err(dev, "Failed to get pcie rc clock\n");
-		return PTR_ERR(exynos_pcie->clk);
-	}
-	ret = clk_prepare_enable(exynos_pcie->clk);
-	if (ret)
-		return ret;
+	exynos_pcie->ops = (const struct exynos_pcie_ops *)
+				of_device_get_match_data(dev);
 
-	exynos_pcie->bus_clk = devm_clk_get(dev, "pcie_bus");
-	if (IS_ERR(exynos_pcie->bus_clk)) {
-		dev_err(dev, "Failed to get pcie bus clock\n");
-		ret = PTR_ERR(exynos_pcie->bus_clk);
-		goto fail_clk;
-	}
-	ret = clk_prepare_enable(exynos_pcie->bus_clk);
-	if (ret)
-		goto fail_clk;
+	exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	exynos_pcie->elbi_base = devm_ioremap_resource(dev, res);
-	if (IS_ERR(exynos_pcie->elbi_base)) {
-		ret = PTR_ERR(exynos_pcie->elbi_base);
-		goto fail_bus_clk;
+	if (exynos_pcie->ops && exynos_pcie->ops->get_mem_resources) {
+		ret = exynos_pcie->ops->get_mem_resources(pdev, exynos_pcie);
+		if (ret)
+			return ret;
 	}
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-	exynos_pcie->phy_base = devm_ioremap_resource(dev, res);
-	if (IS_ERR(exynos_pcie->phy_base)) {
-		ret = PTR_ERR(exynos_pcie->phy_base);
-		goto fail_bus_clk;
-	}
+	if (exynos_pcie->ops && exynos_pcie->ops->get_clk_resources) {
+		ret = exynos_pcie->ops->get_clk_resources(exynos_pcie);
+		if (ret)
+			return ret;
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
-	exynos_pcie->block_base = devm_ioremap_resource(dev, res);
-	if (IS_ERR(exynos_pcie->block_base)) {
-		ret = PTR_ERR(exynos_pcie->block_base);
-		goto fail_bus_clk;
+		ret = exynos_pcie->ops->init_clk_resources(exynos_pcie);
+		if (ret)
+			return ret;
 	}
 
 	ret = exynos_add_pcie_port(exynos_pcie, pdev);
 	if (ret < 0)
-		goto fail_bus_clk;
+		goto fail_probe;
 
 	platform_set_drvdata(pdev, exynos_pcie);
 	return 0;
 
-fail_bus_clk:
-	clk_disable_unprepare(exynos_pcie->bus_clk);
-fail_clk:
-	clk_disable_unprepare(exynos_pcie->clk);
+fail_probe:
+	if (exynos_pcie->ops && exynos_pcie->ops->deinit_clk_resources)
+		exynos_pcie->ops->deinit_clk_resources(exynos_pcie);
 	return ret;
 }
 
@@ -573,14 +660,15 @@ static int __exit exynos_pcie_remove(struct platform_device *pdev)
 {
 	struct exynos_pcie *exynos_pcie = platform_get_drvdata(pdev);
 
-	clk_disable_unprepare(exynos_pcie->bus_clk);
-	clk_disable_unprepare(exynos_pcie->clk);
+	if (exynos_pcie->ops && exynos_pcie->ops->deinit_clk_resources)
+		exynos_pcie->ops->deinit_clk_resources(exynos_pcie);
 
 	return 0;
 }
 
 static const struct of_device_id exynos_pcie_of_match[] = {
-	{ .compatible = "samsung,exynos5440-pcie", },
+	{	.compatible = "samsung,exynos5440-pcie",
+		.data = &exynos5440_pcie_ops },
 	{},
 };
 
-- 
2.7.4

^ permalink raw reply related

* [PATCH v1] watchdog: imx2: fix hang-up on boot for i.MX21, i.MX27 and i.MX31 SoCs
From: Vladimir Zapolskiy @ 2016-12-23 11:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161223100113.s57bi7257yero7f7@pengutronix.de>

Hi Uwe,

On 12/23/2016 12:01 PM, Uwe Kleine-K?nig wrote:
> On Fri, Dec 23, 2016 at 11:27:24AM +0200, Vladimir Zapolskiy wrote:
>> On 12/23/2016 10:32 AM, Uwe Kleine-K?nig wrote:
>>> Hello,
>>>
>>> On Fri, Dec 23, 2016 at 10:20:20AM +0200, Vladimir Zapolskiy wrote:
>>>> On 12/23/2016 03:55 AM, Guenter Roeck wrote:
>>>>> What is the ultimate conclusion of this exchange ?
>>>>>
>>>>> Are we going to get another version of the patch, or did everyone agree that
>>>>> the patch is good as it is and does not require further changes ?
>>>>>
>>>>
>>>> I can not imagine a different fix.
>>>
>>> my preferred fix would be:
>>>
>>>  - add an imx35 compatible to all newer dtsi
>>>  - update the driver to only write the wmcr on imx35 compatible devices
>>>    adding only imx35.
>>>
>>
>> It breaks old DTS vs. new kernel compatibility, I've already mentioned this.
> 
> Correct, and I didn't deny that. In my mail I pointed out the problem
> this imposes and I think it's small enough to not justify the complexity
> introduced in your proposed change.
> 

I can not statistically estimate well the severity of the problem which was
fixed by commit 5fe65ce7cc (all boards with a similar change not found in
a bootloader will be affected, I believe) multiplied by the rate of users,
who don't update DTB. But statistically the probability is non-zero, and
thus it is better to consider that the new problem which I still resist to
introduce will hit somebody.

> If we cannot agree then at least this needs to be better documented in
> the driver because otherwise someone makes a cleanup later dropping all
> the compatibles that are needed to keep backwards compatibility.
> 

My position is to avoid fixing a bug by introducing another known in
advance bug, IMHO it overrules your statement about data redundancy.

Here I'd like to separate responsibilities. If somebody cleans the list
of compatibles up and it accidentally causes problems, the author of that
commit will be blamed and that commit will be reverted, and the revert
commit won't touch my fix -- make i.MX31 boards boot again.

> But my suggestion is to first do the minimal fix, and if in the next
> merge window people lament about breakages on their machine, we can
> still extend the simple fix to a fully backwards compatible change.

Let's fix one problem stated in the commit subject cleanly and without
any known side effects like I suggest, a questionable and optional purge
of compatibles can be done by somebody else later on.

--
With best wishes,
Vladimir

^ permalink raw reply

* [PATCH v2 11/12] crypto: atmel-authenc: add support to authenc(hmac(shaX), Y(aes)) modes
From: Stephan Müller @ 2016-12-23 11:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <2a6cc4637621d2ef0d84754817128c43795cb022.1482424395.git.cyrille.pitchen@atmel.com>

Am Donnerstag, 22. Dezember 2016, 17:38:00 CET schrieb Cyrille Pitchen:

Hi Cyrille,

> This patchs allows to combine the AES and SHA hardware accelerators on
> some Atmel SoCs. Doing so, AES blocks are only written to/read from the
> AES hardware. Those blocks are also transferred from the AES to the SHA
> accelerator internally, without additionnal accesses to the system busses.
> 
> Hence, the AES and SHA accelerators work in parallel to process all the
> data blocks, instead of serializing the process by (de)crypting those
> blocks first then authenticating them after like the generic
> crypto/authenc.c driver does.
> 
> Of course, both the AES and SHA hardware accelerators need to be available
> before we can start to process the data blocks. Hence we use their crypto
> request queue to synchronize both drivers.
> 
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
> ---
>  drivers/crypto/Kconfig          |  12 +
>  drivers/crypto/atmel-aes-regs.h |  16 ++
>  drivers/crypto/atmel-aes.c      | 471
> +++++++++++++++++++++++++++++++++++++++- drivers/crypto/atmel-authenc.h  | 
> 64 ++++++
>  drivers/crypto/atmel-sha-regs.h |  14 ++
>  drivers/crypto/atmel-sha.c      | 344 +++++++++++++++++++++++++++--
>  6 files changed, 906 insertions(+), 15 deletions(-)
>  create mode 100644 drivers/crypto/atmel-authenc.h
> 
> diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
> index 79564785ae30..719a868d8ea1 100644
> --- a/drivers/crypto/Kconfig
> +++ b/drivers/crypto/Kconfig
> @@ -415,6 +415,18 @@ config CRYPTO_DEV_BFIN_CRC
>  	  Newer Blackfin processors have CRC hardware. Select this if you
>  	  want to use the Blackfin CRC module.
> 
> +config CRYPTO_DEV_ATMEL_AUTHENC
> +	tristate "Support for Atmel IPSEC/SSL hw accelerator"
> +	depends on (ARCH_AT91 && HAS_DMA) || COMPILE_TEST
> +	select CRYPTO_AUTHENC
> +	select CRYPTO_DEV_ATMEL_AES
> +	select CRYPTO_DEV_ATMEL_SHA
> +	help
> +	  Some Atmel processors can combine the AES and SHA hw accelerators
> +	  to enhance support of IPSEC/SSL.
> +	  Select this if you want to use the Atmel modules for
> +	  authenc(hmac(shaX),Y(cbc)) algorithms.
> +
>  config CRYPTO_DEV_ATMEL_AES
>  	tristate "Support for Atmel AES hw accelerator"
>  	depends on HAS_DMA
> diff --git a/drivers/crypto/atmel-aes-regs.h
> b/drivers/crypto/atmel-aes-regs.h index 0ec04407b533..7694679802b3 100644
> --- a/drivers/crypto/atmel-aes-regs.h
> +++ b/drivers/crypto/atmel-aes-regs.h
> @@ -68,6 +68,22 @@
>  #define AES_CTRR	0x98
>  #define AES_GCMHR(x)	(0x9c + ((x) * 0x04))
> 
> +#define AES_EMR		0xb0
> +#define AES_EMR_APEN		BIT(0)	/* Auto Padding Enable */
> +#define AES_EMR_APM		BIT(1)	/* Auto Padding Mode */
> +#define AES_EMR_APM_IPSEC	0x0
> +#define AES_EMR_APM_SSL		BIT(1)
> +#define AES_EMR_PLIPEN		BIT(4)	/* PLIP Enable */
> +#define AES_EMR_PLIPD		BIT(5)	/* PLIP Decipher */
> +#define AES_EMR_PADLEN_MASK	(0xFu << 8)
> +#define AES_EMR_PADLEN_OFFSET	8
> +#define AES_EMR_PADLEN(padlen)	(((padlen) << AES_EMR_PADLEN_OFFSET) &\
> +				 AES_EMR_PADLEN_MASK)
> +#define AES_EMR_NHEAD_MASK	(0xFu << 16)
> +#define AES_EMR_NHEAD_OFFSET	16
> +#define AES_EMR_NHEAD(nhead)	(((nhead) << AES_EMR_NHEAD_OFFSET) &\
> +				 AES_EMR_NHEAD_MASK)
> +
>  #define AES_TWR(x)	(0xc0 + ((x) * 0x04))
>  #define AES_ALPHAR(x)	(0xd0 + ((x) * 0x04))
> 
> diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c
> index 9fd2f63b8bc0..3c651e0c3113 100644
> --- a/drivers/crypto/atmel-aes.c
> +++ b/drivers/crypto/atmel-aes.c
> @@ -41,6 +41,7 @@
>  #include <linux/platform_data/crypto-atmel.h>
>  #include <dt-bindings/dma/at91.h>
>  #include "atmel-aes-regs.h"
> +#include "atmel-authenc.h"
> 
>  #define ATMEL_AES_PRIORITY	300
> 
> @@ -78,6 +79,7 @@
>  #define AES_FLAGS_INIT		BIT(2)
>  #define AES_FLAGS_BUSY		BIT(3)
>  #define AES_FLAGS_DUMP_REG	BIT(4)
> +#define AES_FLAGS_OWN_SHA	BIT(5)
> 
>  #define AES_FLAGS_PERSISTENT	(AES_FLAGS_INIT | AES_FLAGS_BUSY)
> 
> @@ -92,6 +94,7 @@ struct atmel_aes_caps {
>  	bool			has_ctr32;
>  	bool			has_gcm;
>  	bool			has_xts;
> +	bool			has_authenc;
>  	u32			max_burst_size;
>  };
> 
> @@ -144,10 +147,31 @@ struct atmel_aes_xts_ctx {
>  	u32			key2[AES_KEYSIZE_256 / sizeof(u32)];
>  };
> 
> +#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
> +struct atmel_aes_authenc_ctx {
> +	struct atmel_aes_base_ctx	base;
> +	struct atmel_sha_authenc_ctx	*auth;
> +};
> +#endif
> +
>  struct atmel_aes_reqctx {
>  	unsigned long		mode;
>  };
> 
> +#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
> +struct atmel_aes_authenc_reqctx {
> +	struct atmel_aes_reqctx	base;
> +
> +	struct scatterlist	src[2];
> +	struct scatterlist	dst[2];
> +	size_t			textlen;
> +	u32			digest[SHA512_DIGEST_SIZE / sizeof(u32)];
> +
> +	/* auth_req MUST be place last. */
> +	struct ahash_request	auth_req;
> +};
> +#endif
> +
>  struct atmel_aes_dma {
>  	struct dma_chan		*chan;
>  	struct scatterlist	*sg;
> @@ -291,6 +315,9 @@ static const char *atmel_aes_reg_name(u32 offset, char
> *tmp, size_t sz) snprintf(tmp, sz, "GCMHR[%u]", (offset - AES_GCMHR(0)) >>
> 2);
>  		break;
> 
> +	case AES_EMR:
> +		return "EMR";
> +
>  	case AES_TWR(0):
>  	case AES_TWR(1):
>  	case AES_TWR(2):
> @@ -463,8 +490,16 @@ static inline bool atmel_aes_is_encrypt(const struct
> atmel_aes_dev *dd) return (dd->flags & AES_FLAGS_ENCRYPT);
>  }
> 
> +#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
> +static void atmel_aes_authenc_complete(struct atmel_aes_dev *dd, int err);
> +#endif
> +
>  static inline int atmel_aes_complete(struct atmel_aes_dev *dd, int err)
>  {
> +#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
> +	atmel_aes_authenc_complete(dd, err);
> +#endif
> +
>  	clk_disable(dd->iclk);
>  	dd->flags &= ~AES_FLAGS_BUSY;
> 
> @@ -1931,6 +1966,407 @@ static struct crypto_alg aes_xts_alg = {
>  	}
>  };
> 
> +#ifdef CONFIG_CRYPTO_DEV_ATMEL_AUTHENC
> +/* authenc aead functions */
> +
> +static int atmel_aes_authenc_start(struct atmel_aes_dev *dd);
> +static int atmel_aes_authenc_init(struct atmel_aes_dev *dd, int err,
> +				  bool is_async);
> +static int atmel_aes_authenc_transfer(struct atmel_aes_dev *dd, int err,
> +				      bool is_async);
> +static int atmel_aes_authenc_digest(struct atmel_aes_dev *dd);
> +static int atmel_aes_authenc_final(struct atmel_aes_dev *dd, int err,
> +				   bool is_async);
> +
> +static void atmel_aes_authenc_complete(struct atmel_aes_dev *dd, int err)
> +{
> +	struct aead_request *req = aead_request_cast(dd->areq);
> +	struct atmel_aes_authenc_reqctx *rctx = aead_request_ctx(req);
> +
> +	if (err && (dd->flags & AES_FLAGS_OWN_SHA))
> +		atmel_sha_authenc_abort(&rctx->auth_req);
> +	dd->flags &= ~AES_FLAGS_OWN_SHA;
> +}
> +
> +static int atmel_aes_authenc_copy_assoc(struct aead_request *req)
> +{
> +	size_t buflen, assoclen = req->assoclen;
> +	off_t skip = 0;
> +	u8 buf[256];
> +
> +	while (assoclen) {
> +		buflen = min_t(size_t, assoclen, sizeof(buf));
> +
> +		if (sg_pcopy_to_buffer(req->src, sg_nents(req->src),
> +				       buf, buflen, skip) != buflen)
> +			return -EINVAL;
> +
> +		if (sg_pcopy_from_buffer(req->dst, sg_nents(req->dst),
> +					 buf, buflen, skip) != buflen)
> +			return -EINVAL;
> +
> +		skip += buflen;
> +		assoclen -= buflen;
> +	}

This seems to be a very expansive operation. Wouldn't it be easier, leaner and 
with one less memcpy to use the approach of crypto_authenc_copy_assoc?

Instead of copying crypto_authenc_copy_assoc, what about carving the logic in 
crypto/authenc.c out into a generic aead helper code as we need to add that to 
other AEAD implementations?
> +
> +	return 0;
> +}
> +
> +static int atmel_aes_authenc_start(struct atmel_aes_dev *dd)
> +{
> +	struct aead_request *req = aead_request_cast(dd->areq);
> +	struct atmel_aes_authenc_reqctx *rctx = aead_request_ctx(req);
> +	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
> +	struct atmel_aes_authenc_ctx *ctx = crypto_aead_ctx(tfm);
> +	int err;
> +
> +	atmel_aes_set_mode(dd, &rctx->base);
> +
> +	err = atmel_aes_hw_init(dd);
> +	if (err)
> +		return atmel_aes_complete(dd, err);
> +
> +	return atmel_sha_authenc_schedule(&rctx->auth_req, ctx->auth,
> +					  atmel_aes_authenc_init, dd);
> +}
> +
> +static int atmel_aes_authenc_init(struct atmel_aes_dev *dd, int err,
> +				  bool is_async)
> +{
> +	struct aead_request *req = aead_request_cast(dd->areq);
> +	struct atmel_aes_authenc_reqctx *rctx = aead_request_ctx(req);
> +
> +	if (is_async)
> +		dd->is_async = true;
> +	if (err)
> +		return atmel_aes_complete(dd, err);
> +
> +	/* If here, we've got the ownership of the SHA device. */
> +	dd->flags |= AES_FLAGS_OWN_SHA;
> +
> +	/* Configure the SHA device. */
> +	return atmel_sha_authenc_init(&rctx->auth_req,
> +				      req->src, req->assoclen,
> +				      rctx->textlen,
> +				      atmel_aes_authenc_transfer, dd);
> +}
> +
> +static int atmel_aes_authenc_transfer(struct atmel_aes_dev *dd, int err,
> +				      bool is_async)
> +{
> +	struct aead_request *req = aead_request_cast(dd->areq);
> +	struct atmel_aes_authenc_reqctx *rctx = aead_request_ctx(req);
> +	bool enc = atmel_aes_is_encrypt(dd);
> +	struct scatterlist *src, *dst;
> +	u32 iv[AES_BLOCK_SIZE / sizeof(u32)];
> +	u32 emr;
> +
> +	if (is_async)
> +		dd->is_async = true;
> +	if (err)
> +		return atmel_aes_complete(dd, err);
> +
> +	/* Prepare src and dst scatter-lists to transfer cipher/plain texts. */
> +	src = scatterwalk_ffwd(rctx->src, req->src, req->assoclen);
> +	dst = src;
> +
> +	if (req->src != req->dst) {
> +		err = atmel_aes_authenc_copy_assoc(req);
> +		if (err)
> +			return atmel_aes_complete(dd, err);
> +
> +		dst = scatterwalk_ffwd(rctx->dst, req->dst, req->assoclen);
> +	}
> +
> +	/* Configure the AES device. */
> +	memcpy(iv, req->iv, sizeof(iv));
> +
> +	/*
> +	 * Here we always set the 2nd parameter of atmel_aes_write_ctrl() to
> +	 * 'true' even if the data transfer is actually performed by the CPU (so
> +	 * not by the DMA) because we must force the AES_MR_SMOD bitfield to the
> +	 * value AES_MR_SMOD_IDATAR0. Indeed, both AES_MR_SMOD and SHA_MR_SMOD
> +	 * must be set to *_MR_SMOD_IDATAR0.
> +	 */
> +	atmel_aes_write_ctrl(dd, true, iv);
> +	emr = AES_EMR_PLIPEN;
> +	if (!enc)
> +		emr |= AES_EMR_PLIPD;
> +	atmel_aes_write(dd, AES_EMR, emr);
> +
> +	/* Transfer data. */
> +	return atmel_aes_dma_start(dd, src, dst, rctx->textlen,
> +				   atmel_aes_authenc_digest);
> +}
> +
> +static int atmel_aes_authenc_digest(struct atmel_aes_dev *dd)
> +{
> +	struct aead_request *req = aead_request_cast(dd->areq);
> +	struct atmel_aes_authenc_reqctx *rctx = aead_request_ctx(req);
> +
> +	/* atmel_sha_authenc_final() releases the SHA device. */
> +	dd->flags &= ~AES_FLAGS_OWN_SHA;
> +	return atmel_sha_authenc_final(&rctx->auth_req,
> +				       rctx->digest, sizeof(rctx->digest),
> +				       atmel_aes_authenc_final, dd);
> +}
> +
> +static int atmel_aes_authenc_final(struct atmel_aes_dev *dd, int err,
> +				   bool is_async)
> +{
> +	struct aead_request *req = aead_request_cast(dd->areq);
> +	struct atmel_aes_authenc_reqctx *rctx = aead_request_ctx(req);
> +	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
> +	bool enc = atmel_aes_is_encrypt(dd);
> +	u32 idigest[SHA512_DIGEST_SIZE / sizeof(u32)], *odigest = rctx->digest;
> +	u32 offs, authsize;
> +
> +	if (is_async)
> +		dd->is_async = true;
> +	if (err)
> +		goto complete;
> +
> +	offs = req->assoclen + rctx->textlen;
> +	authsize = crypto_aead_authsize(tfm);
> +	if (enc) {
> +		scatterwalk_map_and_copy(odigest, req->dst, offs, authsize, 1);
> +	} else {
> +		scatterwalk_map_and_copy(idigest, req->src, offs, authsize, 0);
> +		if (crypto_memneq(idigest, odigest, authsize))
> +			err = -EBADMSG;
> +	}
> +
> +complete:
> +	return atmel_aes_complete(dd, err);
> +}
> +
> +static int atmel_aes_authenc_setkey(struct crypto_aead *tfm, const u8 *key,
> +				    unsigned int keylen)
> +{
> +	struct atmel_aes_authenc_ctx *ctx = crypto_aead_ctx(tfm);
> +	struct crypto_authenc_keys keys;
> +	u32 flags;
> +	int err;
> +
> +	if (crypto_authenc_extractkeys(&keys, key, keylen) != 0)
> +		goto badkey;
> +
> +	if (keys.enckeylen > sizeof(ctx->base.key))
> +		goto badkey;
> +
> +	/* Save auth key. */
> +	flags = crypto_aead_get_flags(tfm);
> +	err = atmel_sha_authenc_setkey(ctx->auth,
> +				       keys.authkey, keys.authkeylen,
> +				       &flags);
> +	crypto_aead_set_flags(tfm, flags & CRYPTO_TFM_RES_MASK);
> +	if (err)
> +		return err;
> +
> +	/* Save enc key. */
> +	ctx->base.keylen = keys.enckeylen;
> +	memcpy(ctx->base.key, keys.enckey, keys.enckeylen);

memzero_explicit(keys) please
> +
> +	return 0;
> +
> +badkey:
> +	crypto_aead_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
> +	return -EINVAL;
> +}
> +
> +static int atmel_aes_authenc_init_tfm(struct crypto_aead *tfm,
> +				      unsigned long auth_mode)
> +{
> +	struct atmel_aes_authenc_ctx *ctx = crypto_aead_ctx(tfm);
> +	unsigned int auth_reqsize = atmel_sha_authenc_get_reqsize();
> +
> +	ctx->auth = atmel_sha_authenc_spawn(auth_mode);
> +	if (IS_ERR(ctx->auth))
> +		return PTR_ERR(ctx->auth);
> +
> +	crypto_aead_set_reqsize(tfm, (sizeof(struct atmel_aes_authenc_reqctx) +
> +				      auth_reqsize));
> +	ctx->base.start = atmel_aes_authenc_start;
> +
> +	return 0;
> +}
> +
> +static int atmel_aes_authenc_hmac_sha1_init_tfm(struct crypto_aead *tfm)
> +{
> +	return atmel_aes_authenc_init_tfm(tfm, SHA_FLAGS_HMAC_SHA1);
> +}
> +
> +static int atmel_aes_authenc_hmac_sha224_init_tfm(struct crypto_aead *tfm)
> +{
> +	return atmel_aes_authenc_init_tfm(tfm, SHA_FLAGS_HMAC_SHA224);
> +}
> +
> +static int atmel_aes_authenc_hmac_sha256_init_tfm(struct crypto_aead *tfm)
> +{
> +	return atmel_aes_authenc_init_tfm(tfm, SHA_FLAGS_HMAC_SHA256);
> +}
> +
> +static int atmel_aes_authenc_hmac_sha384_init_tfm(struct crypto_aead *tfm)
> +{
> +	return atmel_aes_authenc_init_tfm(tfm, SHA_FLAGS_HMAC_SHA384);
> +}
> +
> +static int atmel_aes_authenc_hmac_sha512_init_tfm(struct crypto_aead *tfm)
> +{
> +	return atmel_aes_authenc_init_tfm(tfm, SHA_FLAGS_HMAC_SHA512);
> +}
> +
> +static void atmel_aes_authenc_exit_tfm(struct crypto_aead *tfm)
> +{
> +	struct atmel_aes_authenc_ctx *ctx = crypto_aead_ctx(tfm);
> +
> +	atmel_sha_authenc_free(ctx->auth);
> +}
> +
> +static int atmel_aes_authenc_crypt(struct aead_request *req,
> +				   unsigned long mode)
> +{
> +	struct atmel_aes_authenc_reqctx *rctx = aead_request_ctx(req);
> +	struct crypto_aead *tfm = crypto_aead_reqtfm(req);
> +	struct atmel_aes_base_ctx *ctx = crypto_aead_ctx(tfm);
> +	u32 authsize = crypto_aead_authsize(tfm);
> +	bool enc = (mode & AES_FLAGS_ENCRYPT);
> +	struct atmel_aes_dev *dd;
> +
> +	/* Compute text length. */
> +	rctx->textlen = req->cryptlen - (enc ? 0 : authsize);

Is there somewhere a check that authsize is always < req->cryptlen (at least 
it escaped me)? Note, this logic will be exposed to user space which may do 
funky things.
> +
> +	/*
> +	 * Currently, empty messages are not supported yet:
> +	 * the SHA auto-padding can be used only on non-empty messages.
> +	 * Hence a special case needs to be implemented for empty message.
> +	 */
> +	if (!rctx->textlen && !req->assoclen)
> +		return -EINVAL;
> +
> +	rctx->base.mode = mode;
> +	ctx->block_size = AES_BLOCK_SIZE;
> +
> +	dd = atmel_aes_find_dev(ctx);
> +	if (!dd)
> +		return -ENODEV;
> +
> +	return atmel_aes_handle_queue(dd, &req->base);

Ciao
Stephan

^ permalink raw reply

* [PATCH] mm: pmd dirty emulation in page fault handler
From: Michal Hocko @ 2016-12-23 11:54 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161223095336.GA5305@bbox>

On Fri 23-12-16 18:53:36, Minchan Kim wrote:
> Hi,
> 
> On Fri, Dec 23, 2016 at 10:17:25AM +0100, Michal Hocko wrote:
> > On Thu 22-12-16 23:52:03, Minchan Kim wrote:
> > [...]
> > > >From b3ec95c0df91ad113525968a4a6b53030fd0b48d Mon Sep 17 00:00:00 2001
> > > From: Minchan Kim <minchan@kernel.org>
> > > Date: Thu, 22 Dec 2016 23:43:49 +0900
> > > Subject: [PATCH v2] mm: pmd dirty emulation in page fault handler
> > > 
> > > Andreas reported [1] made a test in jemalloc hang in THP mode in arm64.
> > > http://lkml.kernel.org/r/mvmmvfy37g1.fsf at hawking.suse.de
> > > 
> > > The problem is page fault handler supports only accessed flag emulation
> > > for THP page of SW-dirty/accessed architecture.
> > > 
> > > This patch enables dirty-bit emulation for those architectures.
> > > Without it, MADV_FREE makes application hang by repeated fault forever.
> > 
> > The changelog is rather terse and considering the issue is rather subtle
> > and it aims the stable tree I think it could see more information. How
> > do we end up looping in the page fault and why the dirty pmd stops it.
> > Could you update the changelog to be more verbose, please? I am still
> > digesting this patch but I believe it is correct fwiw...
> > 
> 
> How about this? Feel free to suggest better wording.
> 
> Andreas reported [1] made a test in jemalloc hang in THP mode in arm64.
> http://lkml.kernel.org/r/mvmmvfy37g1.fsf at hawking.suse.de
> 
> The problem is currently page fault handler doesn't supports dirty bit
> emulation of pte for non-HW dirty-bit architecture so that application

s at pte@pmd@ ?

> stucks until VM marked the pmd dirty.
> 
> How the emulation work depends on the architecture. In case of arm64,
> when it set up pte firstly, it sets pte PTE_RDONLY to get a chance to
> mark the pte dirty via triggering page fault when store access happens.
> Once the page fault occurs, VM marks the pte dirty and arch code for
> setting pte will clear PTE_RDONLY for application to proceed.
> 
> IOW, if VM doesn't mark the pte dirty, application hangs forever by
> repeated fault(i.e., store op but the pte is PTE_RDONLY).
> 
> This patch enables dirty-bit emulation for those architectures.

Yes this is helpful and much more clear, thank you. One thing that is
still not clear to me is why cannot we handle that in the arch specific
code. I mean what is the side effect of doing pmd_mkdirty for
architectures which do not need it?

-- 
Michal Hocko
SUSE Labs

^ permalink raw reply

* [PATCH 0/9] Runtime PM for Exynos pin controller driver
From: Marek Szyprowski @ 2016-12-23 12:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CGME20161223122513epcas1p394d93d72d2d32c8910aafd1f6cc6b5e2@epcas1p3.samsung.com>

Hello,

This patchset is a next step to add support for audio power domain on
Exynos5 SoCs.

Audio power domain on Exynos5 SoCs contains following hardware modules:
1. clock controller
2. pin controller
3. PL330 DMA controller
4. I2S audio controller

Till now it was assumed that pin controller is located in the "always on"
power domain and lacked runtime power management. This patch finally
removes such assumption and adds runtime pm support and awareness to this
driver. To achieve this, some changes in the Exynos platform support code
were needed, like moving pad retention control to the pin controller driver.
Some cleanup to the pin controller driver has been also done while changing
the code. This new feature requires some additional information in the
device tree, what is handled by patches 1,2 and 9.

Please note that patches are ordered in such a way that the changes can be
bisected, so the properties are added to dts before the code requiring them.

The other patches related to enabling full support for audio power domain
can be found here:
1. PL330 ADMA controller non-irqsafe runtime PM:
   https://www.spinics.net/lists/arm-kernel/msg550008.html
2. Runtime PM for clock controllers (Exynos Audio subsystem will be added
   in v4 soon): https://www.spinics.net/lists/arm-kernel/msg538122.html

Patches are based on linux-next from 2016.12.22.

Best regards
Marek Szyprowski
Samsung R&D Institute Poland


Patch summary:

Marek Szyprowski (9):
  ARM: dts: exynos: Add PMU syscon to pinctrl nodes
  ARM: dts: exynos: Add pinctrl sleep state for 542x i2s module
  pinctrl: samsung: Remove dead code
  pinctrl: samsung: Use generic of_device_get_match_data helper
  pinctrl: samsung: Move retention control from mach-exynos to the
    pinctrl driver
  pinctrl: samsung: Replace syscore ops with standard platform device
    pm_ops
  pinctrl: samsung: Add property to mark pad state as suitable for power
    down
  pinctrl: samsung: Add runtime PM support
  ARM: dts: exynos: Add audio power domain support to Exynos542x SoCs

 .../bindings/pinctrl/samsung-pinctrl.txt           |  12 ++
 arch/arm/boot/dts/exynos3250.dtsi                  |   2 +
 arch/arm/boot/dts/exynos4210.dtsi                  |   3 +
 arch/arm/boot/dts/exynos4x12.dtsi                  |   3 +
 arch/arm/boot/dts/exynos5250.dtsi                  |   4 +
 arch/arm/boot/dts/exynos5420-pinctrl.dtsi          |  11 ++
 arch/arm/boot/dts/exynos5420.dtsi                  |  18 ++-
 arch/arm/mach-exynos/suspend.c                     |  64 ---------
 drivers/pinctrl/samsung/pinctrl-exynos.c           | 148 +++++++++++++++++++++
 drivers/pinctrl/samsung/pinctrl-samsung.c          | 126 ++++++++----------
 drivers/pinctrl/samsung/pinctrl-samsung.h          |  15 +++
 11 files changed, 271 insertions(+), 135 deletions(-)

-- 
1.9.1

^ permalink raw reply

* [PATCH 1/9] ARM: dts: exynos: Add PMU syscon to pinctrl nodes
From: Marek Szyprowski @ 2016-12-23 12:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1482495889-6201-1-git-send-email-m.szyprowski@samsung.com>

Access to PMU regmap is needed to properly release pad retention after
suspend/resume cycle.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 arch/arm/boot/dts/exynos3250.dtsi | 2 ++
 arch/arm/boot/dts/exynos4210.dtsi | 3 +++
 arch/arm/boot/dts/exynos4x12.dtsi | 3 +++
 arch/arm/boot/dts/exynos5250.dtsi | 4 ++++
 arch/arm/boot/dts/exynos5420.dtsi | 5 +++++
 5 files changed, 17 insertions(+)

diff --git a/arch/arm/boot/dts/exynos3250.dtsi b/arch/arm/boot/dts/exynos3250.dtsi
index ba17ee1eb749..3614b7953fac 100644
--- a/arch/arm/boot/dts/exynos3250.dtsi
+++ b/arch/arm/boot/dts/exynos3250.dtsi
@@ -260,6 +260,7 @@
 			compatible = "samsung,exynos3250-pinctrl";
 			reg = <0x11000000 0x1000>;
 			interrupts = <GIC_SPI 225 IRQ_TYPE_LEVEL_HIGH>;
+			samsung,pmu-syscon = <&pmu_system_controller>;
 
 			wakeup-interrupt-controller {
 				compatible = "samsung,exynos4210-wakeup-eint";
@@ -271,6 +272,7 @@
 			compatible = "samsung,exynos3250-pinctrl";
 			reg = <0x11400000 0x1000>;
 			interrupts = <GIC_SPI 240 IRQ_TYPE_LEVEL_HIGH>;
+			samsung,pmu-syscon = <&pmu_system_controller>;
 		};
 
 		jpeg: codec at 11830000 {
diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi
index 7f3a18c8f60f..c719aaf50f3b 100644
--- a/arch/arm/boot/dts/exynos4210.dtsi
+++ b/arch/arm/boot/dts/exynos4210.dtsi
@@ -128,12 +128,14 @@
 		compatible = "samsung,exynos4210-pinctrl";
 		reg = <0x11400000 0x1000>;
 		interrupts = <GIC_SPI 47 IRQ_TYPE_LEVEL_HIGH>;
+		samsung,pmu-syscon = <&pmu_system_controller>;
 	};
 
 	pinctrl_1: pinctrl at 11000000 {
 		compatible = "samsung,exynos4210-pinctrl";
 		reg = <0x11000000 0x1000>;
 		interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
+		samsung,pmu-syscon = <&pmu_system_controller>;
 
 		wakup_eint: wakeup-interrupt-controller {
 			compatible = "samsung,exynos4210-wakeup-eint";
@@ -145,6 +147,7 @@
 	pinctrl_2: pinctrl at 03860000 {
 		compatible = "samsung,exynos4210-pinctrl";
 		reg = <0x03860000 0x1000>;
+		samsung,pmu-syscon = <&pmu_system_controller>;
 	};
 
 	tmu: tmu at 100C0000 {
diff --git a/arch/arm/boot/dts/exynos4x12.dtsi b/arch/arm/boot/dts/exynos4x12.dtsi
index 85a7122658f1..54b65c4accd6 100644
--- a/arch/arm/boot/dts/exynos4x12.dtsi
+++ b/arch/arm/boot/dts/exynos4x12.dtsi
@@ -546,12 +546,14 @@
 	compatible = "samsung,exynos4x12-pinctrl";
 	reg = <0x11400000 0x1000>;
 	interrupts = <GIC_SPI 47 IRQ_TYPE_LEVEL_HIGH>;
+	samsung,pmu-syscon = <&pmu_system_controller>;
 };
 
 &pinctrl_1 {
 	compatible = "samsung,exynos4x12-pinctrl";
 	reg = <0x11000000 0x1000>;
 	interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
+	samsung,pmu-syscon = <&pmu_system_controller>;
 
 	wakup_eint: wakeup-interrupt-controller {
 		compatible = "samsung,exynos4210-wakeup-eint";
@@ -565,6 +567,7 @@
 	reg = <0x03860000 0x1000>;
 	interrupt-parent = <&combiner>;
 	interrupts = <10 0>;
+	samsung,pmu-syscon = <&pmu_system_controller>;
 };
 
 &pinctrl_3 {
diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi
index b6d7444d8585..ffff28f89589 100644
--- a/arch/arm/boot/dts/exynos5250.dtsi
+++ b/arch/arm/boot/dts/exynos5250.dtsi
@@ -196,6 +196,7 @@
 			compatible = "samsung,exynos5250-pinctrl";
 			reg = <0x11400000 0x1000>;
 			interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
+			samsung,pmu-syscon = <&pmu_system_controller>;
 
 			wakup_eint: wakeup-interrupt-controller {
 				compatible = "samsung,exynos4210-wakeup-eint";
@@ -208,18 +209,21 @@
 			compatible = "samsung,exynos5250-pinctrl";
 			reg = <0x13400000 0x1000>;
 			interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+			samsung,pmu-syscon = <&pmu_system_controller>;
 		};
 
 		pinctrl_2: pinctrl at 10d10000 {
 			compatible = "samsung,exynos5250-pinctrl";
 			reg = <0x10d10000 0x1000>;
 			interrupts = <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>;
+			samsung,pmu-syscon = <&pmu_system_controller>;
 		};
 
 		pinctrl_3: pinctrl at 03860000 {
 			compatible = "samsung,exynos5250-pinctrl";
 			reg = <0x03860000 0x1000>;
 			interrupts = <GIC_SPI 47 IRQ_TYPE_LEVEL_HIGH>;
+			samsung,pmu-syscon = <&pmu_system_controller>;
 		};
 
 		pmu_system_controller: system-controller at 10040000 {
diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi
index 906a1a42a7ea..832cb56c514e 100644
--- a/arch/arm/boot/dts/exynos5420.dtsi
+++ b/arch/arm/boot/dts/exynos5420.dtsi
@@ -321,6 +321,7 @@
 			compatible = "samsung,exynos5420-pinctrl";
 			reg = <0x13400000 0x1000>;
 			interrupts = <0 45 IRQ_TYPE_LEVEL_HIGH>;
+			samsung,pmu-syscon = <&pmu_system_controller>;
 
 			wakeup-interrupt-controller {
 				compatible = "samsung,exynos4210-wakeup-eint";
@@ -333,24 +334,28 @@
 			compatible = "samsung,exynos5420-pinctrl";
 			reg = <0x13410000 0x1000>;
 			interrupts = <0 78 IRQ_TYPE_LEVEL_HIGH>;
+			samsung,pmu-syscon = <&pmu_system_controller>;
 		};
 
 		pinctrl_2: pinctrl at 14000000 {
 			compatible = "samsung,exynos5420-pinctrl";
 			reg = <0x14000000 0x1000>;
 			interrupts = <0 46 IRQ_TYPE_LEVEL_HIGH>;
+			samsung,pmu-syscon = <&pmu_system_controller>;
 		};
 
 		pinctrl_3: pinctrl at 14010000 {
 			compatible = "samsung,exynos5420-pinctrl";
 			reg = <0x14010000 0x1000>;
 			interrupts = <0 50 IRQ_TYPE_LEVEL_HIGH>;
+			samsung,pmu-syscon = <&pmu_system_controller>;
 		};
 
 		pinctrl_4: pinctrl at 03860000 {
 			compatible = "samsung,exynos5420-pinctrl";
 			reg = <0x03860000 0x1000>;
 			interrupts = <0 47 IRQ_TYPE_LEVEL_HIGH>;
+			samsung,pmu-syscon = <&pmu_system_controller>;
 		};
 
 		amba {
-- 
1.9.1

^ permalink raw reply related

* [PATCH 2/9] ARM: dts: exynos: Add pinctrl sleep state for 542x i2s module
From: Marek Szyprowski @ 2016-12-23 12:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1482495889-6201-1-git-send-email-m.szyprowski@samsung.com>

Add a special "sleep" state for Exynos I2S module. This state will be used
to let I2S driver to notify pin controller that it is ready for turning
power off, so the pin controller can also change its runtime state to
suspended and in the result let power domain to turn off.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 arch/arm/boot/dts/exynos5420-pinctrl.dtsi | 11 +++++++++++
 arch/arm/boot/dts/exynos5420.dtsi         |  3 ++-
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/exynos5420-pinctrl.dtsi b/arch/arm/boot/dts/exynos5420-pinctrl.dtsi
index 3924b4fafe72..52983b6a6859 100644
--- a/arch/arm/boot/dts/exynos5420-pinctrl.dtsi
+++ b/arch/arm/boot/dts/exynos5420-pinctrl.dtsi
@@ -720,4 +720,15 @@
 		samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>;
 		samsung,pin-drv = <EXYNOS5420_PIN_DRV_LV1>;
 	};
+
+	i2s0_bus_slp: i2s0-bus-slp {
+		samsung,pins = "gpz-0", "gpz-1", "gpz-2", "gpz-3",
+				"gpz-4", "gpz-5", "gpz-6";
+		samsung,pin-function = <EXYNOS_PIN_FUNC_INPUT>;
+		samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>;
+		samsung,pin-drv = <EXYNOS5420_PIN_DRV_LV1>;
+		samsung,pin-con-pdn = <EXYNOS_PIN_PDN_INPUT>;
+		samsung,pin-pud-pdn = <EXYNOS_PIN_PULL_NONE>;
+		samsung,off-state;
+	};
 };
diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi
index 832cb56c514e..0a7ecdd4c5de 100644
--- a/arch/arm/boot/dts/exynos5420.dtsi
+++ b/arch/arm/boot/dts/exynos5420.dtsi
@@ -444,8 +444,9 @@
 			clock-output-names = "i2s_cdclk0";
 			#sound-dai-cells = <1>;
 			samsung,idma-addr = <0x03000000>;
-			pinctrl-names = "default";
+			pinctrl-names = "default", "sleep";
 			pinctrl-0 = <&i2s0_bus>;
+			pinctrl-1 = <&i2s0_bus_slp>;
 			status = "disabled";
 		};
 
-- 
1.9.1

^ permalink raw reply related

* [PATCH 3/9] pinctrl: samsung: Remove dead code
From: Marek Szyprowski @ 2016-12-23 12:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1482495889-6201-1-git-send-email-m.szyprowski@samsung.com>

'enable' parameter has been removed a while ago, so all code for handling
it can be simply removed.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 drivers/pinctrl/samsung/pinctrl-samsung.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.c b/drivers/pinctrl/samsung/pinctrl-samsung.c
index 41e62391c33c..4d9262051ff1 100644
--- a/drivers/pinctrl/samsung/pinctrl-samsung.c
+++ b/drivers/pinctrl/samsung/pinctrl-samsung.c
@@ -356,7 +356,7 @@ static void pin_to_reg_bank(struct samsung_pinctrl_drv_data *drvdata,
 
 /* enable or disable a pinmux function */
 static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector,
-					unsigned group, bool enable)
+					unsigned group)
 {
 	struct samsung_pinctrl_drv_data *drvdata;
 	const struct samsung_pin_bank_type *type;
@@ -386,8 +386,7 @@ static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector,
 
 	data = readl(reg + type->reg_offset[PINCFG_TYPE_FUNC]);
 	data &= ~(mask << shift);
-	if (enable)
-		data |= func->val << shift;
+	data |= func->val << shift;
 	writel(data, reg + type->reg_offset[PINCFG_TYPE_FUNC]);
 
 	spin_unlock_irqrestore(&bank->slock, flags);
@@ -398,7 +397,7 @@ static int samsung_pinmux_set_mux(struct pinctrl_dev *pctldev,
 				  unsigned selector,
 				  unsigned group)
 {
-	samsung_pinmux_setup(pctldev, selector, group, true);
+	samsung_pinmux_setup(pctldev, selector, group);
 	return 0;
 }
 
-- 
1.9.1

^ permalink raw reply related

* [PATCH 4/9] pinctrl: samsung: Use generic of_device_get_match_data helper
From: Marek Szyprowski @ 2016-12-23 12:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1482495889-6201-1-git-send-email-m.szyprowski@samsung.com>

Replace custom code with generic helper.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 drivers/pinctrl/samsung/pinctrl-samsung.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.c b/drivers/pinctrl/samsung/pinctrl-samsung.c
index 4d9262051ff1..a6c2ea74e0f3 100644
--- a/drivers/pinctrl/samsung/pinctrl-samsung.c
+++ b/drivers/pinctrl/samsung/pinctrl-samsung.c
@@ -27,6 +27,7 @@
 #include <linux/err.h>
 #include <linux/gpio.h>
 #include <linux/irqdomain.h>
+#include <linux/of_device.h>
 #include <linux/spinlock.h>
 #include <linux/syscore_ops.h>
 
@@ -967,15 +968,13 @@ static int samsung_gpiolib_unregister(struct platform_device *pdev,
 	return 0;
 }
 
-static const struct of_device_id samsung_pinctrl_dt_match[];
-
 /* retrieve the soc specific data */
 static const struct samsung_pin_ctrl *
 samsung_pinctrl_get_soc_data(struct samsung_pinctrl_drv_data *d,
 			     struct platform_device *pdev)
 {
 	int id;
-	const struct of_device_id *match;
+	const struct samsung_pin_ctrl *match_data;
 	struct device_node *node = pdev->dev.of_node;
 	struct device_node *np;
 	const struct samsung_pin_bank_data *bdata;
@@ -990,8 +989,8 @@ static int samsung_gpiolib_unregister(struct platform_device *pdev,
 		dev_err(&pdev->dev, "failed to get alias id\n");
 		return ERR_PTR(-ENOENT);
 	}
-	match = of_match_node(samsung_pinctrl_dt_match, node);
-	ctrl = (struct samsung_pin_ctrl *)match->data + id;
+	match_data = of_device_get_match_data(&pdev->dev);
+	ctrl = match_data + id;
 
 	d->suspend = ctrl->suspend;
 	d->resume = ctrl->resume;
-- 
1.9.1

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox