* [PATCH v3 13/18] powerpc/iommu: Extend ppc_md.tce_build(_rm) to return old TCE values
From: Alexey Kardashevskiy @ 2014-07-24 8:48 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Alexey Kardashevskiy, Paul Mackerras, Gavin Shan
In-Reply-To: <1406191691-31441-1-git-send-email-aik@ozlabs.ru>
The tce_build/tce_build_rm callbacks are used to implement H_PUT_TCE/etc
hypercalls. The PAPR spec does not allow to fail if the TCE is not empty.
However we cannot just overwrite the existing TCE value with the new one
as we still have to do page counting.
This adds an optional @old_tces return parameter. If it is not NULL,
it must point to an array of @npages size where the callbacks will
store old TCE values. Since tce_build receives virtual addresses,
the old_tces array will contain virtual addresses as well.
As this patch is mechanical, no change in behaviour is expected.
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Reviewed-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/machdep.h | 2 ++
arch/powerpc/kernel/iommu.c | 8 +++++---
arch/powerpc/platforms/powernv/pci.c | 13 ++++++++-----
arch/powerpc/platforms/pseries/iommu.c | 7 +++++--
arch/powerpc/sysdev/dart_iommu.c | 1 +
5 files changed, 21 insertions(+), 10 deletions(-)
diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h
index f92b0b5..f11596c 100644
--- a/arch/powerpc/include/asm/machdep.h
+++ b/arch/powerpc/include/asm/machdep.h
@@ -69,6 +69,7 @@ struct machdep_calls {
long index,
long npages,
unsigned long uaddr,
+ unsigned long *old_tces,
enum dma_data_direction direction,
struct dma_attrs *attrs);
void (*tce_free)(struct iommu_table *tbl,
@@ -83,6 +84,7 @@ struct machdep_calls {
long index,
long npages,
unsigned long uaddr,
+ long *old_tces,
enum dma_data_direction direction,
struct dma_attrs *attrs);
void (*tce_free_rm)(struct iommu_table *tbl,
diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
index 5af2319..ccf7510 100644
--- a/arch/powerpc/kernel/iommu.c
+++ b/arch/powerpc/kernel/iommu.c
@@ -324,7 +324,8 @@ static dma_addr_t iommu_alloc(struct device *dev, struct iommu_table *tbl,
/* Put the TCEs in the HW table */
build_fail = ppc_md.tce_build(tbl, entry, npages,
(unsigned long)page &
- IOMMU_PAGE_MASK(tbl), direction, attrs);
+ IOMMU_PAGE_MASK(tbl), NULL, direction,
+ attrs);
/* ppc_md.tce_build() only returns non-zero for transient errors.
* Clean up the table bitmap in this case and return
@@ -497,7 +498,7 @@ int iommu_map_sg(struct device *dev, struct iommu_table *tbl,
/* Insert into HW table */
build_fail = ppc_md.tce_build(tbl, entry, npages,
vaddr & IOMMU_PAGE_MASK(tbl),
- direction, attrs);
+ NULL, direction, attrs);
if(unlikely(build_fail))
goto failure;
@@ -1059,7 +1060,8 @@ int iommu_tce_build(struct iommu_table *tbl, unsigned long entry,
oldtce = ppc_md.tce_get(tbl, entry);
/* Add new entry if it is not busy */
if (!(oldtce & (TCE_PCI_WRITE | TCE_PCI_READ)))
- ret = ppc_md.tce_build(tbl, entry, 1, hwaddr, direction, NULL);
+ ret = ppc_md.tce_build(tbl, entry, 1, hwaddr, NULL,
+ direction, NULL);
spin_unlock(&(pool->lock));
diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c
index cc54e3b..4b764c2 100644
--- a/arch/powerpc/platforms/powernv/pci.c
+++ b/arch/powerpc/platforms/powernv/pci.c
@@ -572,7 +572,8 @@ static void pnv_tce_invalidate(struct iommu_table *tbl, __be64 *startp,
}
static int pnv_tce_build(struct iommu_table *tbl, long index, long npages,
- unsigned long uaddr, enum dma_data_direction direction,
+ unsigned long uaddr, unsigned long *old_tces,
+ enum dma_data_direction direction,
struct dma_attrs *attrs, bool rm)
{
u64 proto_tce;
@@ -597,12 +598,12 @@ static int pnv_tce_build(struct iommu_table *tbl, long index, long npages,
}
static int pnv_tce_build_vm(struct iommu_table *tbl, long index, long npages,
- unsigned long uaddr,
+ unsigned long uaddr, unsigned long *old_tces,
enum dma_data_direction direction,
struct dma_attrs *attrs)
{
- return pnv_tce_build(tbl, index, npages, uaddr, direction, attrs,
- false);
+ return pnv_tce_build(tbl, index, npages, uaddr, old_tces, direction,
+ attrs, false);
}
static void pnv_tce_free(struct iommu_table *tbl, long index, long npages,
@@ -630,10 +631,12 @@ static unsigned long pnv_tce_get(struct iommu_table *tbl, long index)
static int pnv_tce_build_rm(struct iommu_table *tbl, long index, long npages,
unsigned long uaddr,
+ long *old_tces,
enum dma_data_direction direction,
struct dma_attrs *attrs)
{
- return pnv_tce_build(tbl, index, npages, uaddr, direction, attrs, true);
+ return pnv_tce_build(tbl, index, npages, uaddr, old_tces, direction,
+ attrs, true);
}
static void pnv_tce_free_rm(struct iommu_table *tbl, long index, long npages)
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index a047754..6c70b7c 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -82,6 +82,7 @@ static void tce_invalidate_pSeries_sw(struct iommu_table *tbl,
static int tce_build_pSeries(struct iommu_table *tbl, long index,
long npages, unsigned long uaddr,
+ unsigned long *old_tces,
enum dma_data_direction direction,
struct dma_attrs *attrs)
{
@@ -138,6 +139,7 @@ static void tce_freemulti_pSeriesLP(struct iommu_table*, long, long);
static int tce_build_pSeriesLP(struct iommu_table *tbl, long tcenum,
long npages, unsigned long uaddr,
+ unsigned long *old_tces,
enum dma_data_direction direction,
struct dma_attrs *attrs)
{
@@ -181,6 +183,7 @@ static DEFINE_PER_CPU(__be64 *, tce_page);
static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
long npages, unsigned long uaddr,
+ unsigned long *old_tces,
enum dma_data_direction direction,
struct dma_attrs *attrs)
{
@@ -195,7 +198,7 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
if (npages == 1) {
return tce_build_pSeriesLP(tbl, tcenum, npages, uaddr,
- direction, attrs);
+ old_tces, direction, attrs);
}
local_irq_save(flags); /* to protect tcep and the page behind it */
@@ -211,7 +214,7 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
if (!tcep) {
local_irq_restore(flags);
return tce_build_pSeriesLP(tbl, tcenum, npages, uaddr,
- direction, attrs);
+ old_tces, direction, attrs);
}
__get_cpu_var(tce_page) = tcep;
}
diff --git a/arch/powerpc/sysdev/dart_iommu.c b/arch/powerpc/sysdev/dart_iommu.c
index 9e5353f..0d3cf7c 100644
--- a/arch/powerpc/sysdev/dart_iommu.c
+++ b/arch/powerpc/sysdev/dart_iommu.c
@@ -162,6 +162,7 @@ static void dart_flush(struct iommu_table *tbl)
static int dart_build(struct iommu_table *tbl, long index,
long npages, unsigned long uaddr,
+ unsigned long *old_tces,
enum dma_data_direction direction,
struct dma_attrs *attrs)
{
--
2.0.0
^ permalink raw reply related
* [PATCH v3 14/18] powerpc/powernv: Return non-zero TCE from pnv_tce_build
From: Alexey Kardashevskiy @ 2014-07-24 8:48 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Alexey Kardashevskiy, Paul Mackerras, Gavin Shan
In-Reply-To: <1406191691-31441-1-git-send-email-aik@ozlabs.ru>
This returns old TCE values to the caller if requested.
The caller is expectded to call put_page() for them.
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Reviewed-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
arch/powerpc/platforms/powernv/pci.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c
index 4b764c2..164d653 100644
--- a/arch/powerpc/platforms/powernv/pci.c
+++ b/arch/powerpc/platforms/powernv/pci.c
@@ -579,6 +579,7 @@ static int pnv_tce_build(struct iommu_table *tbl, long index, long npages,
u64 proto_tce;
__be64 *tcep, *tces;
u64 rpn;
+ long i;
proto_tce = TCE_PCI_READ; // Read allowed
@@ -588,9 +589,13 @@ static int pnv_tce_build(struct iommu_table *tbl, long index, long npages,
tces = tcep = ((__be64 *)tbl->it_base) + index - tbl->it_offset;
rpn = __pa(uaddr) >> tbl->it_page_shift;
- while (npages--)
- *(tcep++) = cpu_to_be64(proto_tce |
- (rpn++ << tbl->it_page_shift));
+ for (i = 0; i < npages; i++) {
+ unsigned long oldtce = xchg(tcep, cpu_to_be64(proto_tce |
+ (rpn++ << tbl->it_page_shift)));
+ if (old_tces)
+ old_tces[i] = (unsigned long) __va(oldtce);
+ tcep++;
+ }
pnv_tce_invalidate(tbl, tces, tcep - 1, rm);
--
2.0.0
^ permalink raw reply related
* [PATCH v3 11/18] powerpc/iommu: Fix IOMMU ownership control functions
From: Alexey Kardashevskiy @ 2014-07-24 8:48 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Alexey Kardashevskiy, Paul Mackerras, Gavin Shan
In-Reply-To: <1406191691-31441-1-git-send-email-aik@ozlabs.ru>
This adds missing locks in iommu_take_ownership()/
iommu_release_ownership().
This marks all pages busy in iommu_table::it_map in order to catch
errors if there is an attempt to use this table while ownership over it
is taken.
This only clears TCE content if there is no page marked busy in it_map.
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
arch/powerpc/kernel/iommu.c | 37 ++++++++++++++++++++++++++++++-------
1 file changed, 30 insertions(+), 7 deletions(-)
diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
index 06984d5..0cda2e8 100644
--- a/arch/powerpc/kernel/iommu.c
+++ b/arch/powerpc/kernel/iommu.c
@@ -1103,33 +1103,56 @@ EXPORT_SYMBOL_GPL(iommu_put_tce_user_mode);
int iommu_take_ownership(struct iommu_table *tbl)
{
- unsigned long sz = (tbl->it_size + 7) >> 3;
+ unsigned long flags, i, sz = (tbl->it_size + 7) >> 3;
+ int ret = 0, bit0 = 0;
+
+ spin_lock_irqsave(&tbl->large_pool.lock, flags);
+ for (i = 0; i < tbl->nr_pools; i++)
+ spin_lock(&tbl->pools[i].lock);
if (tbl->it_offset == 0)
- clear_bit(0, tbl->it_map);
+ bit0 = test_and_clear_bit(0, tbl->it_map);
if (!bitmap_empty(tbl->it_map, tbl->it_size)) {
pr_err("iommu_tce: it_map is not empty");
- return -EBUSY;
+ ret = -EBUSY;
+ if (bit0)
+ set_bit(0, tbl->it_map);
+ } else {
+ memset(tbl->it_map, 0xff, sz);
}
- memset(tbl->it_map, 0xff, sz);
- iommu_clear_tces_and_put_pages(tbl, tbl->it_offset, tbl->it_size);
+ if (!ret)
+ iommu_clear_tces_and_put_pages(tbl, tbl->it_offset,
+ tbl->it_size);
- return 0;
+ for (i = 0; i < tbl->nr_pools; i++)
+ spin_unlock(&tbl->pools[i].lock);
+ spin_unlock_irqrestore(&tbl->large_pool.lock, flags);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(iommu_take_ownership);
void iommu_release_ownership(struct iommu_table *tbl)
{
- unsigned long sz = (tbl->it_size + 7) >> 3;
+ unsigned long flags, i, sz = (tbl->it_size + 7) >> 3;
iommu_clear_tces_and_put_pages(tbl, tbl->it_offset, tbl->it_size);
+
+ spin_lock_irqsave(&tbl->large_pool.lock, flags);
+ for (i = 0; i < tbl->nr_pools; i++)
+ spin_lock(&tbl->pools[i].lock);
+
memset(tbl->it_map, 0, sz);
/* Restore bit#0 set by iommu_init_table() */
if (tbl->it_offset == 0)
set_bit(0, tbl->it_map);
+
+ for (i = 0; i < tbl->nr_pools; i++)
+ spin_unlock(&tbl->pools[i].lock);
+ spin_unlock_irqrestore(&tbl->large_pool.lock, flags);
}
EXPORT_SYMBOL_GPL(iommu_release_ownership);
--
2.0.0
^ permalink raw reply related
* [PATCH v3 15/18] powerpc/iommu: Implement put_page() if TCE had non-zero value
From: Alexey Kardashevskiy @ 2014-07-24 8:48 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Alexey Kardashevskiy, Paul Mackerras, Gavin Shan
In-Reply-To: <1406191691-31441-1-git-send-email-aik@ozlabs.ru>
Guests might put new TCEs without clearing them first and the PAPR spec
allows that.
This adds put_page() for TCEs which we just replaced.
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
arch/powerpc/kernel/iommu.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
index ccf7510..f8bf641 100644
--- a/arch/powerpc/kernel/iommu.c
+++ b/arch/powerpc/kernel/iommu.c
@@ -1057,11 +1057,11 @@ int iommu_tce_build(struct iommu_table *tbl, unsigned long entry,
spin_lock(&(pool->lock));
- oldtce = ppc_md.tce_get(tbl, entry);
- /* Add new entry if it is not busy */
- if (!(oldtce & (TCE_PCI_WRITE | TCE_PCI_READ)))
- ret = ppc_md.tce_build(tbl, entry, 1, hwaddr, NULL,
- direction, NULL);
+ ret = ppc_md.tce_build(tbl, entry, 1, hwaddr, &oldtce,
+ direction, NULL);
+
+ if (oldtce & (TCE_PCI_WRITE | TCE_PCI_READ))
+ put_page(pfn_to_page(__pa(oldtce) >> PAGE_SHIFT));
spin_unlock(&(pool->lock));
--
2.0.0
^ permalink raw reply related
* [PATCH v3 16/18] powerpc/powernv: Implement Dynamic DMA windows (DDW) for IODA
From: Alexey Kardashevskiy @ 2014-07-24 8:48 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Alexey Kardashevskiy, Paul Mackerras, Gavin Shan
In-Reply-To: <1406191691-31441-1-git-send-email-aik@ozlabs.ru>
SPAPR defines an interface to create additional DMA windows dynamically.
"Dynamically" means that the window is not allocated at the guest start
and the guest can request it later. In practice, existing linux guests
check for the capability and if it is there, they create+map one big DMA
window as big as the entire guest RAM.
SPAPR defines 4 RTAS calls for this feature which userspace implements.
This adds 4 callbacks into the spapr_tce_iommu_ops struct:
1. query - ibm,query-pe-dma-window - returns number/size of windows
which can be created (one, any page size);
2. create - ibm,create-pe-dma-window - creates a window;
3. remove - ibm,remove-pe-dma-window - removes a window; only additional
window created by create() can be removed, the default 32bit window cannot
be removed as guests do not expect new windows to start from zero;
4. reset - ibm,reset-pe-dma-window - reset the DMA windows configuration
to the default state; now it only removes the additional window if it
was created.
The next patch will add corresponding ioctls to VFIO SPAPR TCE driver to
pass RTAS call from the userspace to the IODA code.
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
arch/powerpc/include/asm/tce.h | 21 ++++
arch/powerpc/platforms/powernv/pci-ioda.c | 157 +++++++++++++++++++++++++++++-
arch/powerpc/platforms/powernv/pci.h | 2 +
3 files changed, 179 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/include/asm/tce.h b/arch/powerpc/include/asm/tce.h
index 5ee4987..583463b 100644
--- a/arch/powerpc/include/asm/tce.h
+++ b/arch/powerpc/include/asm/tce.h
@@ -60,6 +60,27 @@ struct spapr_tce_iommu_ops {
phys_addr_t addr);
void (*take_ownership)(struct spapr_tce_iommu_group *data,
bool enable);
+
+ /* Dynamic DMA window */
+ /* Page size flags for ibm,query-pe-dma-window */
+#define DDW_PGSIZE_4K 0x01
+#define DDW_PGSIZE_64K 0x02
+#define DDW_PGSIZE_16M 0x04
+#define DDW_PGSIZE_32M 0x08
+#define DDW_PGSIZE_64M 0x10
+#define DDW_PGSIZE_128M 0x20
+#define DDW_PGSIZE_256M 0x40
+#define DDW_PGSIZE_16G 0x80
+ long (*query)(struct spapr_tce_iommu_group *data,
+ __u32 *windows_available,
+ __u32 *page_size_mask);
+ long (*create)(struct spapr_tce_iommu_group *data,
+ __u32 page_shift,
+ __u32 window_shift,
+ struct iommu_table **ptbl);
+ long (*remove)(struct spapr_tce_iommu_group *data,
+ struct iommu_table *tbl);
+ long (*reset)(struct spapr_tce_iommu_group *data);
};
struct spapr_tce_iommu_group {
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
index f828c57..2f2bdab 100644
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
@@ -754,6 +754,24 @@ static void pnv_pci_ioda2_setup_bypass_pe(struct pnv_phb *phb,
pnv_pci_ioda2_set_bypass(pe, true);
}
+static struct iommu_table *pnv_ioda2_iommu_get_table(
+ struct spapr_tce_iommu_group *data,
+ phys_addr_t addr)
+{
+ struct pnv_ioda_pe *pe = data->iommu_owner;
+
+ if (addr == TCE_DEFAULT_WINDOW)
+ return &pe->tce32.table;
+
+ if (pnv_pci_ioda_check_addr(&pe->tce64.table, addr))
+ return &pe->tce64.table;
+
+ if (pnv_pci_ioda_check_addr(&pe->tce32.table, addr))
+ return &pe->tce32.table;
+
+ return NULL;
+}
+
static void pnv_ioda2_take_ownership(struct spapr_tce_iommu_group *data,
bool enable)
{
@@ -762,9 +780,146 @@ static void pnv_ioda2_take_ownership(struct spapr_tce_iommu_group *data,
pnv_pci_ioda2_set_bypass(pe, !enable);
}
+static long pnv_pci_ioda2_ddw_query(struct spapr_tce_iommu_group *data,
+ __u32 *windows_available, __u32 *page_size_mask)
+{
+ struct pnv_ioda_pe *pe = data->iommu_owner;
+
+ if (pe->tce64_active) {
+ *page_size_mask = 0;
+ *windows_available = 0;
+ } else {
+ *page_size_mask =
+ DDW_PGSIZE_4K |
+ DDW_PGSIZE_64K |
+ DDW_PGSIZE_16M;
+ *windows_available = 1;
+ }
+
+ return 0;
+}
+
+static long pnv_pci_ioda2_ddw_create(struct spapr_tce_iommu_group *data,
+ __u32 page_shift, __u32 window_shift,
+ struct iommu_table **ptbl)
+{
+ struct pnv_ioda_pe *pe = data->iommu_owner;
+ struct pnv_phb *phb = pe->phb;
+ struct page *tce_mem = NULL;
+ void *addr;
+ long ret;
+ unsigned long tce_table_size =
+ (1ULL << (window_shift - page_shift)) * 8;
+ unsigned order;
+ struct iommu_table *tbl64 = &pe->tce64.table;
+
+ if ((page_shift != 12) && (page_shift != 16) && (page_shift != 24))
+ return -EINVAL;
+
+ if (window_shift > (memory_hotplug_max() >> page_shift))
+ return -EINVAL;
+
+ if (pe->tce64_active)
+ return -EBUSY;
+
+ tce_table_size = max(0x1000UL, tce_table_size);
+ order = get_order(tce_table_size);
+
+ pe_info(pe, "Setting up DDW at %llx..%llx ws=0x%x ps=0x%x table_size=0x%lx order=0x%x\n",
+ pe->tce_bypass_base,
+ pe->tce_bypass_base + (1ULL << window_shift) - 1,
+ window_shift, page_shift, tce_table_size, order);
+
+ tce_mem = alloc_pages_node(phb->hose->node, GFP_KERNEL, order);
+ if (!tce_mem) {
+ pe_err(pe, " Failed to allocate a DDW\n");
+ return -EFAULT;
+ }
+ addr = page_address(tce_mem);
+ memset(addr, 0, tce_table_size);
+
+ /* Configure HW */
+ ret = opal_pci_map_pe_dma_window(phb->opal_id,
+ pe->pe_number,
+ (pe->pe_number << 1) + 1, /* Window number */
+ 1,
+ __pa(addr),
+ tce_table_size,
+ 1 << page_shift);
+ if (ret) {
+ pe_err(pe, " Failed to configure 32-bit TCE table, err %ld\n",
+ ret);
+ return -EFAULT;
+ }
+
+ /* Setup linux iommu table */
+ pnv_pci_setup_iommu_table(tbl64, addr, tce_table_size,
+ pe->tce_bypass_base, page_shift);
+ pe->tce64.pe = pe;
+
+ /* Copy "invalidate" register address */
+ tbl64->it_index = pe->tce32.table.it_index;
+ tbl64->it_type = TCE_PCI_SWINV_CREATE | TCE_PCI_SWINV_FREE |
+ TCE_PCI_SWINV_PAIR;
+ tbl64->it_map = (void *) 0xDEADBEEF; /* poison */
+
+ *ptbl = &pe->tce64.table;
+
+ pe->tce64_active = true;
+
+ return 0;
+}
+
+static long pnv_pci_ioda2_ddw_remove(struct spapr_tce_iommu_group *data,
+ struct iommu_table *tbl)
+{
+ struct pnv_ioda_pe *pe = data->iommu_owner;
+ struct pnv_phb *phb = pe->phb;
+ long ret;
+
+ /* Only additional 64bit window removal is supported */
+ if ((tbl != &pe->tce64.table) || !pe->tce64_active)
+ return -EFAULT;
+
+ pe_info(pe, "Removing huge 64bit DMA window\n");
+
+ iommu_clear_tces_and_put_pages(tbl, tbl->it_offset, tbl->it_size);
+
+ pe->tce64_active = false;
+
+ ret = opal_pci_map_pe_dma_window(phb->opal_id,
+ pe->pe_number,
+ (pe->pe_number << 1) + 1,
+ 0/* levels */, 0/* table address */,
+ 0/* table size */, 0/* page size */);
+ if (ret)
+ pe_warn(pe, "Unmapping failed, ret = %ld\n", ret);
+
+ free_pages(tbl->it_base, get_order(tbl->it_size << 3));
+ memset(&pe->tce64, 0, sizeof(pe->tce64));
+
+ return ret;
+}
+
+static long pnv_pci_ioda2_ddw_reset(struct spapr_tce_iommu_group *data)
+{
+ struct pnv_ioda_pe *pe = data->iommu_owner;
+
+ pe_info(pe, "Reset DMA windows\n");
+
+ if (!pe->tce64_active)
+ return 0;
+
+ return pnv_pci_ioda2_ddw_remove(data, &pe->tce64.table);
+}
+
static struct spapr_tce_iommu_ops pnv_pci_ioda2_ops = {
- .get_table = pnv_ioda1_iommu_get_table,
+ .get_table = pnv_ioda2_iommu_get_table,
.take_ownership = pnv_ioda2_take_ownership,
+ .query = pnv_pci_ioda2_ddw_query,
+ .create = pnv_pci_ioda2_ddw_create,
+ .remove = pnv_pci_ioda2_ddw_remove,
+ .reset = pnv_pci_ioda2_ddw_reset
};
static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb,
diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h
index 32847a5..7e88d8a 100644
--- a/arch/powerpc/platforms/powernv/pci.h
+++ b/arch/powerpc/platforms/powernv/pci.h
@@ -64,6 +64,8 @@ struct pnv_ioda_pe {
int tce32_segcount;
struct pnv_iommu_table tce32;
phys_addr_t tce_inval_reg_phys;
+ bool tce64_active;
+ struct pnv_iommu_table tce64;
/* 64-bit TCE bypass region */
bool tce_bypass_enabled;
--
2.0.0
^ permalink raw reply related
* [PATCH v3 17/18] vfio: Use it_page_size
From: Alexey Kardashevskiy @ 2014-07-24 8:48 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Alexey Kardashevskiy, Paul Mackerras, Gavin Shan
In-Reply-To: <1406191691-31441-1-git-send-email-aik@ozlabs.ru>
This makes use of the it_page_size from the iommu_table struct
as page size can differ.
This replaces missing IOMMU_PAGE_SHIFT macro in commented debug code
as recently introduced IOMMU_PAGE_XXX macros do not include
IOMMU_PAGE_SHIFT.
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
drivers/vfio/vfio_iommu_spapr_tce.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
index 05b2f11..2deab4a 100644
--- a/drivers/vfio/vfio_iommu_spapr_tce.c
+++ b/drivers/vfio/vfio_iommu_spapr_tce.c
@@ -59,7 +59,7 @@ static long tce_iommu_account_memlimit(struct iommu_table *tbl, bool inc)
if (!current->mm)
return -ESRCH; /* process exited */
- npages = (tbl->it_size << IOMMU_PAGE_SHIFT_4K) >> PAGE_SHIFT;
+ npages = (tbl->it_size << tbl->it_page_shift) >> PAGE_SHIFT;
if (!inc)
npages = -npages;
@@ -228,8 +228,8 @@ static long tce_iommu_ioctl(void *iommu_data,
if (info.argsz < minsz)
return -EINVAL;
- info.dma32_window_start = tbl->it_offset << IOMMU_PAGE_SHIFT_4K;
- info.dma32_window_size = tbl->it_size << IOMMU_PAGE_SHIFT_4K;
+ info.dma32_window_start = tbl->it_offset << tbl->it_page_shift;
+ info.dma32_window_size = tbl->it_size << tbl->it_page_shift;
info.flags = 0;
if (copy_to_user((void __user *)arg, &info, minsz))
@@ -282,17 +282,17 @@ static long tce_iommu_ioctl(void *iommu_data,
if (ret)
return ret;
- for (i = 0; i < (param.size >> IOMMU_PAGE_SHIFT_4K); ++i) {
+ for (i = 0; i < (param.size >> tbl->it_page_shift); ++i) {
ret = iommu_put_tce_user_mode(tbl,
- (param.iova >> IOMMU_PAGE_SHIFT_4K) + i,
+ (param.iova >> tbl->it_page_shift) + i,
tce);
if (ret)
break;
- tce += IOMMU_PAGE_SIZE_4K;
+ tce += IOMMU_PAGE_SIZE(tbl);
}
if (ret)
iommu_clear_tces_and_put_pages(tbl,
- param.iova >> IOMMU_PAGE_SHIFT_4K, i);
+ param.iova >> tbl->it_page_shift, i);
iommu_flush_tce(tbl);
@@ -333,13 +333,13 @@ static long tce_iommu_ioctl(void *iommu_data,
BUG_ON(!tbl->it_group);
ret = iommu_tce_clear_param_check(tbl, param.iova, 0,
- param.size >> IOMMU_PAGE_SHIFT_4K);
+ param.size >> tbl->it_page_shift);
if (ret)
return ret;
ret = iommu_clear_tces_and_put_pages(tbl,
- param.iova >> IOMMU_PAGE_SHIFT_4K,
- param.size >> IOMMU_PAGE_SHIFT_4K);
+ param.iova >> tbl->it_page_shift,
+ param.size >> tbl->it_page_shift);
iommu_flush_tce(tbl);
return ret;
--
2.0.0
^ permalink raw reply related
* [PATCH v3 18/18] vfio: powerpc: Enable Dynamic DMA windows
From: Alexey Kardashevskiy @ 2014-07-24 8:48 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Alexey Kardashevskiy, Paul Mackerras, Gavin Shan
In-Reply-To: <1406191691-31441-1-git-send-email-aik@ozlabs.ru>
This defines and implements VFIO IOMMU API required to support
Dynamic DMA windows defined in the SPAPR specification. The ioctl handlers
implement host-size part of corresponding RTAS calls:
- VFIO_IOMMU_SPAPR_TCE_QUERY - ibm,query-pe-dma-window;
- VFIO_IOMMU_SPAPR_TCE_CREATE - ibm,create-pe-dma-window;
- VFIO_IOMMU_SPAPR_TCE_REMOVE - ibm,remove-pe-dma-window;
- VFIO_IOMMU_SPAPR_TCE_RESET - ibm,reset-pe-dma-window.
The VFIO IOMMU driver does basic sanity checks and calls corresponding
SPAPR TCE functions. At the moment only IODA2 (POWER8 PCI host bridge)
implements them.
This advertises VFIO_IOMMU_SPAPR_TCE_FLAG_DDW capability via
VFIO_IOMMU_SPAPR_TCE_GET_INFO.
This calls reset() when IOMMU is being disabled (happens when VFIO stops
using it).
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
arch/powerpc/platforms/powernv/pci-ioda.c | 1 +
drivers/vfio/vfio_iommu_spapr_tce.c | 169 +++++++++++++++++++++++++++++-
include/uapi/linux/vfio.h | 37 ++++++-
3 files changed, 205 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
index 2f2bdab..abd97be 100644
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
@@ -859,6 +859,7 @@ static long pnv_pci_ioda2_ddw_create(struct spapr_tce_iommu_group *data,
/* Copy "invalidate" register address */
tbl64->it_index = pe->tce32.table.it_index;
+ tbl64->it_group = pe->tce32.table.it_group;
tbl64->it_type = TCE_PCI_SWINV_CREATE | TCE_PCI_SWINV_FREE |
TCE_PCI_SWINV_PAIR;
tbl64->it_map = (void *) 0xDEADBEEF; /* poison */
diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
index 2deab4a..20d34b8 100644
--- a/drivers/vfio/vfio_iommu_spapr_tce.c
+++ b/drivers/vfio/vfio_iommu_spapr_tce.c
@@ -45,6 +45,7 @@ struct tce_container {
struct mutex lock;
struct iommu_group *grp;
bool enabled;
+ unsigned long start64;
};
/*
@@ -145,18 +146,34 @@ static void tce_iommu_disable(struct tce_container *container)
container->enabled = false;
- if (!container->grp || !current->mm)
+ if (!container->grp)
return;
data = iommu_group_get_iommudata(container->grp);
if (!data || !data->iommu_owner || !data->ops->get_table)
return;
+ /* Try resetting, there might have been a 64bit window */
+ if (data->ops->reset)
+ data->ops->reset(data);
+
+ if (!current->mm)
+ return;
+
tbl = data->ops->get_table(data, TCE_DEFAULT_WINDOW);
if (!tbl)
return;
tce_iommu_account_memlimit(tbl, false);
+
+ if (!container->start64)
+ return;
+
+ tbl = data->ops->get_table(data, container->start64);
+ if (!tbl)
+ return;
+
+ tce_iommu_account_memlimit(tbl, false);
}
static void *tce_iommu_open(unsigned long arg)
@@ -231,6 +248,8 @@ static long tce_iommu_ioctl(void *iommu_data,
info.dma32_window_start = tbl->it_offset << tbl->it_page_shift;
info.dma32_window_size = tbl->it_size << tbl->it_page_shift;
info.flags = 0;
+ if (data->ops->query && data->ops->create && data->ops->remove)
+ info.flags |= VFIO_IOMMU_SPAPR_TCE_FLAG_DDW;
if (copy_to_user((void __user *)arg, &info, minsz))
return -EFAULT;
@@ -356,6 +375,154 @@ static long tce_iommu_ioctl(void *iommu_data,
tce_iommu_disable(container);
mutex_unlock(&container->lock);
return 0;
+
+ case VFIO_IOMMU_SPAPR_TCE_QUERY: {
+ struct vfio_iommu_spapr_tce_query query;
+ struct spapr_tce_iommu_group *data;
+
+ if (WARN_ON(!container->grp))
+ return -ENXIO;
+
+ data = iommu_group_get_iommudata(container->grp);
+
+ minsz = offsetofend(struct vfio_iommu_spapr_tce_query,
+ page_size_mask);
+
+ if (copy_from_user(&query, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (query.argsz < minsz)
+ return -EINVAL;
+
+ if (!data->ops->query || !data->iommu_owner)
+ return -ENOSYS;
+
+ ret = data->ops->query(data,
+ &query.windows_available,
+ &query.page_size_mask);
+
+ if (ret)
+ return ret;
+
+ if (copy_to_user((void __user *)arg, &query, minsz))
+ return -EFAULT;
+
+ return 0;
+ }
+ case VFIO_IOMMU_SPAPR_TCE_CREATE: {
+ struct vfio_iommu_spapr_tce_create create;
+ struct spapr_tce_iommu_group *data;
+ struct iommu_table *tbl;
+
+ if (WARN_ON(!container->grp))
+ return -ENXIO;
+
+ data = iommu_group_get_iommudata(container->grp);
+
+ minsz = offsetofend(struct vfio_iommu_spapr_tce_create,
+ start_addr);
+
+ if (copy_from_user(&create, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (create.argsz < minsz)
+ return -EINVAL;
+
+ if (!data->ops->create || !data->iommu_owner)
+ return -ENOSYS;
+
+ BUG_ON(!data || !data->ops || !data->ops->remove);
+
+ ret = data->ops->create(data, create.page_shift,
+ create.window_shift, &tbl);
+ if (ret)
+ return ret;
+
+ ret = tce_iommu_account_memlimit(tbl, true);
+ if (ret) {
+ data->ops->remove(data, tbl);
+ return ret;
+ }
+
+ create.start_addr = tbl->it_offset << tbl->it_page_shift;
+
+ if (copy_to_user((void __user *)arg, &create, minsz)) {
+ data->ops->remove(data, tbl);
+ tce_iommu_account_memlimit(tbl, false);
+ return -EFAULT;
+ }
+
+ return ret;
+ }
+ case VFIO_IOMMU_SPAPR_TCE_REMOVE: {
+ struct vfio_iommu_spapr_tce_remove remove;
+ struct spapr_tce_iommu_group *data;
+ struct iommu_table *tbl;
+
+ if (WARN_ON(!container->grp))
+ return -ENXIO;
+
+ data = iommu_group_get_iommudata(container->grp);
+
+ minsz = offsetofend(struct vfio_iommu_spapr_tce_remove,
+ start_addr);
+
+ if (copy_from_user(&remove, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (remove.argsz < minsz)
+ return -EINVAL;
+
+ if (!data->ops->remove || !data->iommu_owner)
+ return -ENOSYS;
+
+ tbl = data->ops->get_table(data, remove.start_addr);
+ if (!tbl)
+ return -EINVAL;
+
+ ret = data->ops->remove(data, tbl);
+ if (ret)
+ return ret;
+
+ tce_iommu_account_memlimit(tbl, false);
+
+ return 0;
+ }
+ case VFIO_IOMMU_SPAPR_TCE_RESET: {
+ struct vfio_iommu_spapr_tce_reset reset;
+ struct spapr_tce_iommu_group *data;
+
+ if (WARN_ON(!container->grp))
+ return -ENXIO;
+
+ data = iommu_group_get_iommudata(container->grp);
+
+ minsz = offsetofend(struct vfio_iommu_spapr_tce_reset, argsz);
+
+ if (copy_from_user(&reset, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (reset.argsz < minsz)
+ return -EINVAL;
+
+ if (!data->ops->reset || !data->iommu_owner)
+ return -ENOSYS;
+
+ ret = data->ops->reset(data);
+ if (ret)
+ return ret;
+
+ if (container->start64) {
+ struct iommu_table *tbl;
+
+ tbl = data->ops->get_table(data, container->start64);
+ BUG_ON(!tbl);
+
+ tce_iommu_account_memlimit(tbl, false);
+ }
+
+ return 0;
+ }
}
return -ENOTTY;
diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
index cb9023d..8b03381 100644
--- a/include/uapi/linux/vfio.h
+++ b/include/uapi/linux/vfio.h
@@ -448,13 +448,48 @@ struct vfio_iommu_type1_dma_unmap {
*/
struct vfio_iommu_spapr_tce_info {
__u32 argsz;
- __u32 flags; /* reserved for future use */
+ __u32 flags;
+#define VFIO_IOMMU_SPAPR_TCE_FLAG_DDW 1 /* Support dynamic windows */
__u32 dma32_window_start; /* 32 bit window start (bytes) */
__u32 dma32_window_size; /* 32 bit window size (bytes) */
};
#define VFIO_IOMMU_SPAPR_TCE_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 12)
+/*
+ * Dynamic DMA windows
+ */
+struct vfio_iommu_spapr_tce_query {
+ __u32 argsz;
+ /* out */
+ __u32 windows_available;
+ __u32 page_size_mask;
+};
+#define VFIO_IOMMU_SPAPR_TCE_QUERY _IO(VFIO_TYPE, VFIO_BASE + 17)
+
+struct vfio_iommu_spapr_tce_create {
+ __u32 argsz;
+ /* in */
+ __u32 page_shift;
+ __u32 window_shift;
+ /* out */
+ __u64 start_addr;
+
+};
+#define VFIO_IOMMU_SPAPR_TCE_CREATE _IO(VFIO_TYPE, VFIO_BASE + 18)
+
+struct vfio_iommu_spapr_tce_remove {
+ __u32 argsz;
+ /* in */
+ __u64 start_addr;
+};
+#define VFIO_IOMMU_SPAPR_TCE_REMOVE _IO(VFIO_TYPE, VFIO_BASE + 19)
+
+struct vfio_iommu_spapr_tce_reset {
+ __u32 argsz;
+};
+#define VFIO_IOMMU_SPAPR_TCE_RESET _IO(VFIO_TYPE, VFIO_BASE + 20)
+
/* ***************************************************************** */
#endif /* _UAPIVFIO_H */
--
2.0.0
^ permalink raw reply related
* [PATCH v3 01/18] powerpc/iommu: Fix comments with it_page_shift
From: Alexey Kardashevskiy @ 2014-07-24 8:47 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Alexey Kardashevskiy, Paul Mackerras, Gavin Shan
In-Reply-To: <1406191691-31441-1-git-send-email-aik@ozlabs.ru>
There is a couple of commented debug prints which still use
IOMMU_PAGE_SHIFT() which is not defined for POWERPC anymore, replace
them with it_page_shift.
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Reviewed-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
arch/powerpc/kernel/iommu.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
index 88e3ec6..f84f799 100644
--- a/arch/powerpc/kernel/iommu.c
+++ b/arch/powerpc/kernel/iommu.c
@@ -1037,7 +1037,7 @@ int iommu_tce_build(struct iommu_table *tbl, unsigned long entry,
/* if (unlikely(ret))
pr_err("iommu_tce: %s failed on hwaddr=%lx ioba=%lx kva=%lx ret=%d\n",
- __func__, hwaddr, entry << IOMMU_PAGE_SHIFT(tbl),
+ __func__, hwaddr, entry << tbl->it_page_shift,
hwaddr, ret); */
return ret;
@@ -1056,7 +1056,7 @@ int iommu_put_tce_user_mode(struct iommu_table *tbl, unsigned long entry,
direction != DMA_TO_DEVICE, &page);
if (unlikely(ret != 1)) {
/* pr_err("iommu_tce: get_user_pages_fast failed tce=%lx ioba=%lx ret=%d\n",
- tce, entry << IOMMU_PAGE_SHIFT(tbl), ret); */
+ tce, entry << tbl->it_page_shift, ret); */
return -EFAULT;
}
hwaddr = (unsigned long) page_address(page) + offset;
--
2.0.0
^ permalink raw reply related
* RE: [PATCH 1/6 v2] KVM: PPC: Book3E: Use common defines for SPE/FP/AltiVec int numbers
From: mihai.caraman @ 2014-07-24 9:16 UTC (permalink / raw)
To: Scott Wood
Cc: linuxppc-dev@lists.ozlabs.org, Alexander Graf,
kvm-ppc@vger.kernel.org, kvm@vger.kernel.org
In-Reply-To: <767ce3456f6f40809a1488b4df90498d@BLUPR03MB503.namprd03.prod.outlook.com>
> -----Original Message-----
> From: kvm-ppc-owner@vger.kernel.org [mailto:kvm-ppc-
> owner@vger.kernel.org] On Behalf Of mihai.caraman@freescale.com
> Sent: Monday, July 21, 2014 4:23 PM
> To: Alexander Graf; Wood Scott-B07421
> Cc: kvm-ppc@vger.kernel.org; kvm@vger.kernel.org; linuxppc-
> dev@lists.ozlabs.org
> Subject: RE: [PATCH 1/6 v2] KVM: PPC: Book3E: Use common defines for
> SPE/FP/AltiVec int numbers
>=20
> > -----Original Message-----
> > From: Alexander Graf [mailto:agraf@suse.de]
> > Sent: Thursday, July 03, 2014 3:21 PM
> > To: Caraman Mihai Claudiu-B02008; kvm-ppc@vger.kernel.org
> > Cc: kvm@vger.kernel.org; linuxppc-dev@lists.ozlabs.org
> > Subject: Re: [PATCH 1/6 v2] KVM: PPC: Book3E: Use common defines for
> > SPE/FP/AltiVec int numbers
> >
> >
> > On 30.06.14 17:34, Mihai Caraman wrote:
> > > Use common BOOKE_IRQPRIO and BOOKE_INTERRUPT defines for
> SPE/FP/AltiVec
> > > which share the same interrupt numbers.
> > >
> > > Signed-off-by: Mihai Caraman <mihai.caraman@freescale.com>
> > > ---
> > > v2:
> > > - remove outdated definitions
> > >
> > > arch/powerpc/include/asm/kvm_asm.h | 8 --------
> > > arch/powerpc/kvm/booke.c | 17 +++++++++--------
> > > arch/powerpc/kvm/booke.h | 4 ++--
> > > arch/powerpc/kvm/booke_interrupts.S | 9 +++++----
> > > arch/powerpc/kvm/bookehv_interrupts.S | 4 ++--
> > > arch/powerpc/kvm/e500.c | 10 ++++++----
> > > arch/powerpc/kvm/e500_emulate.c | 10 ++++++----
> > > 7 files changed, 30 insertions(+), 32 deletions(-)
> > >
> > > diff --git a/arch/powerpc/include/asm/kvm_asm.h
> > b/arch/powerpc/include/asm/kvm_asm.h
> > > index 9601741..c94fd33 100644
> > > --- a/arch/powerpc/include/asm/kvm_asm.h
> > > +++ b/arch/powerpc/include/asm/kvm_asm.h
> > > @@ -56,14 +56,6 @@
> > > /* E500 */
> > > #define BOOKE_INTERRUPT_SPE_ALTIVEC_UNAVAIL 32
> > > #define BOOKE_INTERRUPT_SPE_FP_DATA_ALTIVEC_ASSIST 33
> > > -/*
> > > - * TODO: Unify 32-bit and 64-bit kernel exception handlers to use
> same
> > defines
> > > - */
> > > -#define BOOKE_INTERRUPT_SPE_UNAVAIL
> > BOOKE_INTERRUPT_SPE_ALTIVEC_UNAVAIL
> > > -#define BOOKE_INTERRUPT_SPE_FP_DATA
> > BOOKE_INTERRUPT_SPE_FP_DATA_ALTIVEC_ASSIST
> > > -#define BOOKE_INTERRUPT_ALTIVEC_UNAVAIL
> > BOOKE_INTERRUPT_SPE_ALTIVEC_UNAVAIL
> > > -#define BOOKE_INTERRUPT_ALTIVEC_ASSIST \
> > > - BOOKE_INTERRUPT_SPE_FP_DATA_ALTIVEC_ASSIST
> >
> > I think I'd prefer to keep them separate.
> >
> > > #define BOOKE_INTERRUPT_SPE_FP_ROUND 34
> > > #define BOOKE_INTERRUPT_PERFORMANCE_MONITOR 35
> > > #define BOOKE_INTERRUPT_DOORBELL 36
> > > diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
> > > index ab62109..3c86d9b 100644
> > > --- a/arch/powerpc/kvm/booke.c
> > > +++ b/arch/powerpc/kvm/booke.c
> > > @@ -388,8 +388,8 @@ static int kvmppc_booke_irqprio_deliver(struct
> > kvm_vcpu *vcpu,
> > > case BOOKE_IRQPRIO_ITLB_MISS:
> > > case BOOKE_IRQPRIO_SYSCALL:
> > > case BOOKE_IRQPRIO_FP_UNAVAIL:
> > > - case BOOKE_IRQPRIO_SPE_UNAVAIL:
> > > - case BOOKE_IRQPRIO_SPE_FP_DATA:
> > > + case BOOKE_IRQPRIO_SPE_ALTIVEC_UNAVAIL:
> > > + case BOOKE_IRQPRIO_SPE_FP_DATA_ALTIVEC_ASSIST:
> >
> > #ifdef CONFIG_KVM_E500V2
> > case ...SPE:
> > #else
> > case ..ALTIVEC:
> > #endif
> >
> > > case BOOKE_IRQPRIO_SPE_FP_ROUND:
> > > case BOOKE_IRQPRIO_AP_UNAVAIL:
> > > allowed =3D 1;
> > > @@ -977,18 +977,19 @@ int kvmppc_handle_exit(struct kvm_run *run,
> > struct kvm_vcpu *vcpu,
> > > break;
> > >
> > > #ifdef CONFIG_SPE
> > > - case BOOKE_INTERRUPT_SPE_UNAVAIL: {
> > > + case BOOKE_INTERRUPT_SPE_ALTIVEC_UNAVAIL: {
> > > if (vcpu->arch.shared->msr & MSR_SPE)
> > > kvmppc_vcpu_enable_spe(vcpu);
> > > else
> > > kvmppc_booke_queue_irqprio(vcpu,
> > > - BOOKE_IRQPRIO_SPE_UNAVAIL);
> > > + BOOKE_IRQPRIO_SPE_ALTIVEC_UNAVAIL);
> > > r =3D RESUME_GUEST;
> > > break;
> > > }
> > >
> > > - case BOOKE_INTERRUPT_SPE_FP_DATA:
> > > - kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_SPE_FP_DATA);
> > > + case BOOKE_INTERRUPT_SPE_FP_DATA_ALTIVEC_ASSIST:
> > > + kvmppc_booke_queue_irqprio(vcpu,
> > > + BOOKE_IRQPRIO_SPE_FP_DATA_ALTIVEC_ASSIST);
> > > r =3D RESUME_GUEST;
> > > break;
> > >
> > > @@ -997,7 +998,7 @@ int kvmppc_handle_exit(struct kvm_run *run,
> struct
> > kvm_vcpu *vcpu,
> > > r =3D RESUME_GUEST;
> > > break;
> > > #else
> > > - case BOOKE_INTERRUPT_SPE_UNAVAIL:
> > > + case BOOKE_INTERRUPT_SPE_ALTIVEC_UNAVAIL:
> > > /*
> > > * Guest wants SPE, but host kernel doesn't support it.
> Send
> > > * an "unimplemented operation" program check to the
> guest.
> > > @@ -1010,7 +1011,7 @@ int kvmppc_handle_exit(struct kvm_run *run,
> > struct kvm_vcpu *vcpu,
> > > * These really should never happen without CONFIG_SPE,
> > > * as we should never enable the real MSR[SPE] in the guest.
> > > */
> > > - case BOOKE_INTERRUPT_SPE_FP_DATA:
> > > + case BOOKE_INTERRUPT_SPE_FP_DATA_ALTIVEC_ASSIST:
> > > case BOOKE_INTERRUPT_SPE_FP_ROUND:
> > > printk(KERN_CRIT "%s: unexpected SPE interrupt %u at
> > %08lx\n",
> > > __func__, exit_nr, vcpu->arch.pc);
> > > diff --git a/arch/powerpc/kvm/booke.h b/arch/powerpc/kvm/booke.h
> > > index b632cd3..f182b32 100644
> > > --- a/arch/powerpc/kvm/booke.h
> > > +++ b/arch/powerpc/kvm/booke.h
> > > @@ -32,8 +32,8 @@
> > > #define BOOKE_IRQPRIO_ALIGNMENT 2
> > > #define BOOKE_IRQPRIO_PROGRAM 3
> > > #define BOOKE_IRQPRIO_FP_UNAVAIL 4
> > > -#define BOOKE_IRQPRIO_SPE_UNAVAIL 5
> > > -#define BOOKE_IRQPRIO_SPE_FP_DATA 6
> > > +#define BOOKE_IRQPRIO_SPE_ALTIVEC_UNAVAIL 5
> > > +#define BOOKE_IRQPRIO_SPE_FP_DATA_ALTIVEC_ASSIST 6
> >
> > #ifdef CONFIG_KVM_E500V2
> > #define ...SPE...
> > #else
> > #define ...ALTIVEC...
> > #endif
> >
> > That way we can be 100% sure that no SPE cruft leaks into anything.
> >
> >
> > > #define BOOKE_IRQPRIO_SPE_FP_ROUND 7
> > > #define BOOKE_IRQPRIO_SYSCALL 8
> > > #define BOOKE_IRQPRIO_AP_UNAVAIL 9
> > > diff --git a/arch/powerpc/kvm/booke_interrupts.S
> > b/arch/powerpc/kvm/booke_interrupts.S
> > > index 2c6deb5ef..a275dc5 100644
> > > --- a/arch/powerpc/kvm/booke_interrupts.S
> > > +++ b/arch/powerpc/kvm/booke_interrupts.S
> > > @@ -137,8 +137,9 @@ KVM_HANDLER BOOKE_INTERRUPT_WATCHDOG
> > SPRN_SPRG_RSCRATCH_CRIT SPRN_CSRR0
> > > KVM_HANDLER BOOKE_INTERRUPT_DTLB_MISS SPRN_SPRG_RSCRATCH0 SPRN_SRR0
> > > KVM_HANDLER BOOKE_INTERRUPT_ITLB_MISS SPRN_SPRG_RSCRATCH0 SPRN_SRR0
> > > KVM_DBG_HANDLER BOOKE_INTERRUPT_DEBUG SPRN_SPRG_RSCRATCH_CRIT
> > SPRN_CSRR0
> > > -KVM_HANDLER BOOKE_INTERRUPT_SPE_UNAVAIL SPRN_SPRG_RSCRATCH0
> SPRN_SRR0
> > > -KVM_HANDLER BOOKE_INTERRUPT_SPE_FP_DATA SPRN_SPRG_RSCRATCH0
> SPRN_SRR0
> > > +KVM_HANDLER BOOKE_INTERRUPT_SPE_ALTIVEC_UNAVAIL SPRN_SPRG_RSCRATCH0
> > SPRN_SRR0
> > > +KVM_HANDLER BOOKE_INTERRUPT_SPE_FP_DATA_ALTIVEC_ASSIST
> > SPRN_SPRG_RSCRATCH0 \
> > > + SPRN_SRR0
> >
> >
> > Same thing here - just only trap SPE when CONFIG_KVM_E500V2 is
> available
> > and trap altivec otherwise (to make sure we always have a handler).
>=20
>
> This will not even build with current kernel. 32-bit FSL kernel defines
> SPE
> handlers for e500v2/e500mc/e5500 (see head_fsl_booke.S which is guarded
> by
> CONFIG_FSL_BOOKE) even when CONFIG_SPE is not defined. This is simple to
> verify by removing KVM's HV 32-bit BOOKE_INTERRUPT_SPE_xxx handlers from
> bookehv_interrupts.S.
>=20
> The kernel equivalent of your CONFIG_KVM_E500V2 suggestion looks like
> this:
>=20
> #ifndef CONFIG_PPC_E500MC
> /* e500v2 */
> #define BOOKE_INTERRUPT_SPE_UNAVAIL 32
> #define BOOKE_INTERRUPT_SPE_FP_DATA 33
> #define BOOKE_INTERRUPT_SPE_FP_ROUND 34
> #elif
> /* e500mc, e5500, e6500 */
> #define BOOKE_INTERRUPT_ALTIVEC_UNAVAIL 32
> #define BOOKE_INTERRUPT_ALTIVEC_ASSIST BOOKE_33
> #endif
>=20
> but instead, the current kernel expects something like this:
>=20
> #ifdef CONFIG_FSL_BOOKE
> /* 32-bit targets: e500v2, e500mc, e5500 */
> #define BOOKE_INTERRUPT_SPE_UNAVAIL 32
> #define BOOKE_INTERRUPT_SPE_FP_DATA 33
> #define BOOKE_INTERRUPT_SPE_FP_ROUND 34
> #elif CONFIG_PPC_BOOK3E_64
> /* 64-bit targets: e5500, e6500 */
> #define BOOKE_INTERRUPT_ALTIVEC_UNAVAIL 32
> #define BOOKE_INTERRUPT_ALTIVEC_ASSIST BOOKE_33
> #endif
>=20
> We can guard kernel SPE handlers with !CONFIG_PPC_E500MC to have
> something like:
>=20
> #ifdef CONFIG_FSL_BOOKE
> #ifndef CONFIG_PPC_E500MC
> /* e500v2 */
> #define BOOKE_INTERRUPT_SPE_UNAVAIL 32
> #define BOOKE_INTERRUPT_SPE_FP_DATA 33
> #define BOOKE_INTERRUPT_SPE_FP_ROUND 34
> #endif
> #elif CONFIG_PPC_BOOK3E_64
> /* e5500, e6500 */
> #define BOOKE_INTERRUPT_ALTIVEC_UNAVAIL 32
> #define BOOKE_INTERRUPT_ALTIVEC_ASSIST BOOKE_33
> #endif
>=20
> My suggestion is to go ahead with KVM AltiVec patches without waiting for
> this kernel cleanup. I will do the KVM synchronization later when you
> will
> have these kernel changes in your tree.
>=20
> Scott, do you agree to guard SPE kernel handlers in head_fsl_booke.S with
> !CONFIG_PPC_E500MC?
>=20
> -Mike
Scott, Alex's request to define SPE handlers only for e500v2 implies change=
s
in 32-bit FSL kernel to have exclusive configurations for e200/e500v2 and
e500mc/e5500. We would probably need something like this, what's your take =
on it?
diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/hea=
d_fsl_booke.S
index b497188..9d41015 100644
--- a/arch/powerpc/kernel/head_fsl_booke.S
+++ b/arch/powerpc/kernel/head_fsl_booke.S
@@ -613,6 +613,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_EMB_HV)
mfspr r10, SPRN_SPRG_RSCRATCH0
b InstructionStorage
=20
+/* Define SPE handlers only for e500v2 and e200 */
+#ifndef CONFIG_PPC_E500MC
#ifdef CONFIG_SPE
/* SPE Unavailable */
START_EXCEPTION(SPEUnavailable)
@@ -626,7 +628,9 @@ END_FTR_SECTION_IFSET(CPU_FTR_EMB_HV)
EXCEPTION(0x2020, SPE_ALTIVEC_UNAVAIL, SPEUnavailable, \
unknown_exception, EXC_XFER_EE)
#endif /* CONFIG_SPE */
+#endif
=20
+#ifndef CONFIG_PPC_E500MC
/* SPE Floating Point Data */
#ifdef CONFIG_SPE
EXCEPTION(0x2030, SPE_FP_DATA_ALTIVEC_ASSIST, SPEFloatingPointData,
@@ -641,6 +645,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_EMB_HV)
EXCEPTION(0x2050, SPE_FP_ROUND, SPEFloatingPointRound, \
unknown_exception, EXC_XFER_EE)
#endif /* CONFIG_SPE */
+#endif
=20
/* Performance Monitor */
EXCEPTION(0x2060, PERFORMANCE_MONITOR, PerformanceMonitor, \
@@ -947,6 +952,7 @@ get_phys_addr:
* Global functions
*/
=20
+#ifdef CONFIG_E200
/* Adjust or setup IVORs for e200 */
_GLOBAL(__setup_e200_ivors)
li r3,DebugDebug@l
@@ -959,7 +965,9 @@ _GLOBAL(__setup_e200_ivors)
mtspr SPRN_IVOR34,r3
sync
blr
+#endif
=20
+#ifndef CONFIG_PPC_E500MC
/* Adjust or setup IVORs for e500v1/v2 */
_GLOBAL(__setup_e500_ivors)
li r3,DebugCrit@l
@@ -974,6 +982,7 @@ _GLOBAL(__setup_e500_ivors)
mtspr SPRN_IVOR35,r3
sync
blr
+#endif
=20
/* Adjust or setup IVORs for e500mc */
_GLOBAL(__setup_e500mc_ivors)
diff --git a/arch/powerpc/kernel/cpu_setup_fsl_booke.S b/arch/powerpc/kerne=
l/cpu_setup_fsl_booke.S
index cc2d896..32afb50 100644
--- a/arch/powerpc/kernel/cpu_setup_fsl_booke.S
+++ b/arch/powerpc/kernel/cpu_setup_fsl_booke.S
@@ -109,12 +109,16 @@ _GLOBAL(__setup_cpu_e6500)
blr
=20
#ifdef CONFIG_PPC32
+#ifdef CONFIG_E200
_GLOBAL(__setup_cpu_e200)
/* enable dedicated debug exception handling resources (Debug APU) =
*/
mfspr r3,SPRN_HID0
ori r3,r3,HID0_DAPUEN@l
mtspr SPRN_HID0,r3
b __setup_e200_ivors
+#endif /* CONFIG_E200 */
+#ifdef CONFIG_E500
+#ifndef CONFIG_PPC_E500MC
_GLOBAL(__setup_cpu_e500v1)
_GLOBAL(__setup_cpu_e500v2)
mflr r4
@@ -129,6 +133,8 @@ _GLOBAL(__setup_cpu_e500v2)
#endif
mtlr r4
blr
+#endif /* !CONFIG_PPC_E500MC */
+
_GLOBAL(__setup_cpu_e500mc)
_GLOBAL(__setup_cpu_e5500)
mflr r5
@@ -223,3 +229,4 @@ _GLOBAL(__setup_cpu_e5500)
mtlr r5
blr
#endif
+#endif /* CONFIG_E500 */
diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.=
c
index c1faade..3ab65c2 100644
--- a/arch/powerpc/kernel/cputable.c
+++ b/arch/powerpc/kernel/cputable.c
@@ -2030,6 +2030,7 @@ static struct cpu_spec __initdata cpu_specs[] =3D {
#endif /* CONFIG_PPC32 */
#ifdef CONFIG_E500
#ifdef CONFIG_PPC32
+#ifndef CONFIG_PPC_E500MC
{ /* e500 */
.pvr_mask =3D 0xffff0000,
.pvr_value =3D 0x80200000,
@@ -2069,6 +2070,7 @@ static struct cpu_spec __initdata cpu_specs[] =3D {
.machine_check =3D machine_check_e500,
.platform =3D "ppc8548",
},
+#endif /* !CONFIG_PPC_E500MC */
{ /* e500mc */
.pvr_mask =3D 0xffff0000,
.pvr_value =3D 0x80230000,
-Mike
^ permalink raw reply related
* Re: [PATCH v6 0/5] Read guest last instruction from kvmppc_get_last_inst()
From: Alexander Graf @ 2014-07-24 13:08 UTC (permalink / raw)
To: Mihai Caraman, kvm-ppc; +Cc: linuxppc-dev, kvm
In-Reply-To: <1406131582-6052-1-git-send-email-mihai.caraman@freescale.com>
On 23.07.14 18:06, Mihai Caraman wrote:
> Read guest last instruction from kvmppc_get_last_inst() allowing the function
> to fail in order to emulate again. On bookehv architecture search for
> the physical address and kmap it, instead of using Load External PID (lwepx)
> instruction. This fixes an infinite loop caused by lwepx's data TLB miss
> exception handled in the host and the TODO for execute-but-not-read entries
> and TLB eviction.
>
> Mihai Caraman (5):
> KVM: PPC: e500mc: Revert "add load inst fixup"
> KVM: PPC: Book3e: Add TLBSEL/TSIZE defines for MAS0/1
> KVM: PPC: Book3s: Remove kvmppc_read_inst() function
> KVM: PPC: Alow kvmppc_get_last_inst() to fail
> KVM: PPC: Bookehv: Get vcpu's last instruction for emulation
Thanks, applied to kvm-ppc-queue.
Alex
^ permalink raw reply
* [PATCH] powerpc: fix wrong defintion in boot/io.h
From: Lucas Tanure @ 2014-07-24 13:24 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, linuxppc-dev,
linux-kernel, trivial, kernelnewbies
Fix wrong __IO_H definition in boot/io.h
Reported-by: Fernando Silveira <fsilveira@gmail.com>
Signed-off-by: Lucas Tanure <tanure@linux.com>
---
arch/powerpc/boot/io.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/powerpc/boot/io.h b/arch/powerpc/boot/io.h
index 7c09f48..394da55 100644
--- a/arch/powerpc/boot/io.h
+++ b/arch/powerpc/boot/io.h
@@ -1,5 +1,5 @@
#ifndef _IO_H
-#define __IO_H
+#define _IO_H
#include "types.h"
--
2.0.0
^ permalink raw reply related
* Re: [PATCH v2 1/2] ARM: imx: Add the secondary request into the structure for imx-sdma
From: Shawn Guo @ 2014-07-24 13:30 UTC (permalink / raw)
To: Nicolin Chen
Cc: mark.rutland, devicetree, alsa-devel, b42378, timur, pawel.moll,
ijc+devicetree, tiwai, linux-kernel, rdunlap, b02247, lgirdwood,
robh+dt, perex, broonie, galak, grant.likely, linuxppc-dev
In-Reply-To: <e47c36814695fd8c88ef063a9771cf097a128ec0.1406189745.git.nicoleotsuka@gmail.com>
On Thu, Jul 24, 2014 at 04:35:28PM +0800, Nicolin Chen wrote:
> SDMA supports device to device (per_2_per) scripts to handle DMA transfering
> between two peripheral devices. The per_2_per script, however, needs two dma
> requests from two sides while the current structure only defined one request.
>
> So this patch just simply adds the secondary request so as to let SDMA and
> its user to add its implementation later.
>
> [ Both change in the SDMA driver and its users like Freescale ASRC ASoC driver
> should be taken along with this change in order to truly support per_2_per
> sciprts. However, we here make an expediency by adding this first so that
> we can add either side later since this patch won't break any function and
> meanwhile it can make merge window more smoothly: we don't need to apply the
> change inside dmaengine branch via ASoC tree any more. -- Nicolin ]
>
> Signed-off-by: Nicolin Chen <nicoleotsuka@gmail.com>
Acked-by: Shawn Guo <shawn.guo@linaro.org>
^ permalink raw reply
* Re: [PATCH 1/4] powerpc: implement vmemmap_list_free()
From: Nathan Fontenot @ 2014-07-24 15:11 UTC (permalink / raw)
To: Li Zhong, linuxppc-dev; +Cc: paulus
In-Reply-To: <1402475019-19699-1-git-send-email-zhong@linux.vnet.ibm.com>
On 06/11/2014 03:23 AM, Li Zhong wrote:
> This patch implements vmemmap_list_free() for vmemmap_free().
>
> The freed entries will be removed from vmemmap_list, and form a freed list,
> with next as the header. The next position in the last allocated page is kept
> at the list tail.
>
> When allocation, if there are freed entries left, get it from the freed list;
> if no freed entries left, get it like before from the last allocated pages.
>
> With this change, realmode_pfn_to_page() also needs to be changed to walk
> all the entries in the vmemmap_list, as the virt_addr of the entries might not
> be stored in order anymore.
>
> It helps to reuse the memory when continuous doing memory hot-plug/remove
> operations, but didn't reclaim the pages already allocated, so the memory usage
> will only increase, but won't exceed the value for the largest memory
> configuration.
>
> Signed-off-by: Li Zhong <zhong@linux.vnet.ibm.com>
> Cc: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Acked-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
> ---
> arch/powerpc/mm/init_64.c | 62 +++++++++++++++++++++++++++++++++++++--------
> 1 file changed, 52 insertions(+), 10 deletions(-)
>
> diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c
> index e3734ed..fa5d28b 100644
> --- a/arch/powerpc/mm/init_64.c
> +++ b/arch/powerpc/mm/init_64.c
> @@ -226,14 +226,24 @@ static void __meminit vmemmap_create_mapping(unsigned long start,
> #endif /* CONFIG_PPC_BOOK3E */
>
> struct vmemmap_backing *vmemmap_list;
> +static struct vmemmap_backing *next;
> +static int num_left;
> +static int num_freed;
>
> static __meminit struct vmemmap_backing * vmemmap_list_alloc(int node)
> {
> - static struct vmemmap_backing *next;
> - static int num_left;
> + struct vmemmap_backing *vmem_back;
> + /* get from freed entries first */
> + if (num_freed) {
> + num_freed--;
> + vmem_back = next;
> + next = next->list;
> +
> + return vmem_back;
> + }
>
> /* allocate a page when required and hand out chunks */
> - if (!next || !num_left) {
> + if (!num_left) {
> next = vmemmap_alloc_block(PAGE_SIZE, node);
> if (unlikely(!next)) {
> WARN_ON(1);
> @@ -266,6 +276,38 @@ static __meminit void vmemmap_list_populate(unsigned long phys,
> vmemmap_list = vmem_back;
> }
>
> +static unsigned long vmemmap_list_free(unsigned long start)
> +{
> + struct vmemmap_backing *vmem_back, *vmem_back_prev;
> +
> + vmem_back_prev = vmem_back = vmemmap_list;
> +
> + /* look for it with prev pointer recorded */
> + for (; vmem_back; vmem_back = vmem_back->list) {
> + if (vmem_back->virt_addr == start)
> + break;
> + vmem_back_prev = vmem_back;
> + }
> +
> + if (unlikely(!vmem_back)) {
> + WARN_ON(1);
> + return 0;
> + }
> +
> + /* remove it from vmemmap_list */
> + if (vmem_back == vmemmap_list) /* remove head */
> + vmemmap_list = vmem_back->list;
> + else
> + vmem_back_prev->list = vmem_back->list;
> +
> + /* next point to this freed entry */
> + vmem_back->list = next;
> + next = vmem_back;
> + num_freed++;
> +
> + return vmem_back->phys;
> +}
> +
> int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node)
> {
> unsigned long page_size = 1 << mmu_psize_defs[mmu_vmemmap_psize].shift;
> @@ -331,16 +373,16 @@ struct page *realmode_pfn_to_page(unsigned long pfn)
> if (pg_va < vmem_back->virt_addr)
> continue;
>
> - /* Check that page struct is not split between real pages */
> - if ((pg_va + sizeof(struct page)) >
> - (vmem_back->virt_addr + page_size))
> - return NULL;
> -
> - page = (struct page *) (vmem_back->phys + pg_va -
> + /* After vmemmap_list entry free is possible, need check all */
> + if ((pg_va + sizeof(struct page)) <=
> + (vmem_back->virt_addr + page_size)) {
> + page = (struct page *) (vmem_back->phys + pg_va -
> vmem_back->virt_addr);
> - return page;
> + return page;
> + }
> }
>
> + /* Probably that page struct is split between real pages */
> return NULL;
> }
> EXPORT_SYMBOL_GPL(realmode_pfn_to_page);
>
^ permalink raw reply
* Re: [PATCH 2/4] powerpc: implement vmemmap_remove_mapping() for BOOK3S
From: Nathan Fontenot @ 2014-07-24 15:12 UTC (permalink / raw)
To: Li Zhong, linuxppc-dev; +Cc: paulus
In-Reply-To: <1402475019-19699-2-git-send-email-zhong@linux.vnet.ibm.com>
On 06/11/2014 03:23 AM, Li Zhong wrote:
> This is to be called in vmemmap_free(), leave the implementation on BOOK3E
> empty as before.
>
> Signed-off-by: Li Zhong <zhong@linux.vnet.ibm.com>
> Cc: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Acked-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
> ---
> arch/powerpc/mm/hash_utils_64.c | 2 +-
> arch/powerpc/mm/init_64.c | 22 ++++++++++++++++++++++
> 2 files changed, 23 insertions(+), 1 deletion(-)
>
> diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c
> index 88fdd9d..25d9d66 100644
> --- a/arch/powerpc/mm/hash_utils_64.c
> +++ b/arch/powerpc/mm/hash_utils_64.c
> @@ -243,7 +243,7 @@ int htab_bolt_mapping(unsigned long vstart, unsigned long vend,
> }
>
> #ifdef CONFIG_MEMORY_HOTPLUG
> -static int htab_remove_mapping(unsigned long vstart, unsigned long vend,
> +int htab_remove_mapping(unsigned long vstart, unsigned long vend,
> int psize, int ssize)
> {
> unsigned long vaddr;
> diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c
> index fa5d28b..69203c8 100644
> --- a/arch/powerpc/mm/init_64.c
> +++ b/arch/powerpc/mm/init_64.c
> @@ -212,6 +212,13 @@ static void __meminit vmemmap_create_mapping(unsigned long start,
> for (i = 0; i < page_size; i += PAGE_SIZE)
> BUG_ON(map_kernel_page(start + i, phys, flags));
> }
> +
> +#ifdef CONFIG_MEMORY_HOTPLUG
> +static void vmemmap_remove_mapping(unsigned long start,
> + unsigned long page_size)
> +{
> +}
> +#endif
> #else /* CONFIG_PPC_BOOK3E */
> static void __meminit vmemmap_create_mapping(unsigned long start,
> unsigned long page_size,
> @@ -223,6 +230,21 @@ static void __meminit vmemmap_create_mapping(unsigned long start,
> mmu_kernel_ssize);
> BUG_ON(mapped < 0);
> }
> +
> +#ifdef CONFIG_MEMORY_HOTPLUG
> +extern int htab_remove_mapping(unsigned long vstart, unsigned long vend,
> + int psize, int ssize);
> +
> +static void vmemmap_remove_mapping(unsigned long start,
> + unsigned long page_size)
> +{
> + int mapped = htab_remove_mapping(start, start + page_size,
> + mmu_vmemmap_psize,
> + mmu_kernel_ssize);
> + BUG_ON(mapped < 0);
> +}
> +#endif
> +
> #endif /* CONFIG_PPC_BOOK3E */
>
> struct vmemmap_backing *vmemmap_list;
>
^ permalink raw reply
* Re: [PATCH 3/4] powerpc: implement vmemmap_free()
From: Nathan Fontenot @ 2014-07-24 15:12 UTC (permalink / raw)
To: Li Zhong, linuxppc-dev; +Cc: paulus
In-Reply-To: <1402475019-19699-3-git-send-email-zhong@linux.vnet.ibm.com>
On 06/11/2014 03:23 AM, Li Zhong wrote:
> vmemmap_free() does the opposite of vmemap_populate().
> This patch also puts vmemmap_free() and vmemmap_list_free() into
> CONFIG_MEMMORY_HOTPLUG.
>
> Signed-off-by: Li Zhong <zhong@linux.vnet.ibm.com>
> Cc: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Acked-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
> ---
> arch/powerpc/mm/init_64.c | 85 ++++++++++++++++++++++++++++++++++-----------
> 1 file changed, 64 insertions(+), 21 deletions(-)
>
> diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c
> index 69203c8..4963790 100644
> --- a/arch/powerpc/mm/init_64.c
> +++ b/arch/powerpc/mm/init_64.c
> @@ -298,6 +298,37 @@ static __meminit void vmemmap_list_populate(unsigned long phys,
> vmemmap_list = vmem_back;
> }
>
> +int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node)
> +{
> + unsigned long page_size = 1 << mmu_psize_defs[mmu_vmemmap_psize].shift;
> +
> + /* Align to the page size of the linear mapping. */
> + start = _ALIGN_DOWN(start, page_size);
> +
> + pr_debug("vmemmap_populate %lx..%lx, node %d\n", start, end, node);
> +
> + for (; start < end; start += page_size) {
> + void *p;
> +
> + if (vmemmap_populated(start, page_size))
> + continue;
> +
> + p = vmemmap_alloc_block(page_size, node);
> + if (!p)
> + return -ENOMEM;
> +
> + vmemmap_list_populate(__pa(p), start, node);
> +
> + pr_debug(" * %016lx..%016lx allocated at %p\n",
> + start, start + page_size, p);
> +
> + vmemmap_create_mapping(start, page_size, __pa(p));
> + }
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_MEMORY_HOTPLUG
> static unsigned long vmemmap_list_free(unsigned long start)
> {
> struct vmemmap_backing *vmem_back, *vmem_back_prev;
> @@ -330,40 +361,52 @@ static unsigned long vmemmap_list_free(unsigned long start)
> return vmem_back->phys;
> }
>
> -int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node)
> +void __ref vmemmap_free(unsigned long start, unsigned long end)
> {
> unsigned long page_size = 1 << mmu_psize_defs[mmu_vmemmap_psize].shift;
>
> - /* Align to the page size of the linear mapping. */
> start = _ALIGN_DOWN(start, page_size);
>
> - pr_debug("vmemmap_populate %lx..%lx, node %d\n", start, end, node);
> + pr_debug("vmemmap_free %lx...%lx\n", start, end);
>
> for (; start < end; start += page_size) {
> - void *p;
> + unsigned long addr;
>
> + /*
> + * the section has already be marked as invalid, so
> + * vmemmap_populated() true means some other sections still
> + * in this page, so skip it.
> + */
> if (vmemmap_populated(start, page_size))
> continue;
>
> - p = vmemmap_alloc_block(page_size, node);
> - if (!p)
> - return -ENOMEM;
> -
> - vmemmap_list_populate(__pa(p), start, node);
> -
> - pr_debug(" * %016lx..%016lx allocated at %p\n",
> - start, start + page_size, p);
> -
> - vmemmap_create_mapping(start, page_size, __pa(p));
> + addr = vmemmap_list_free(start);
> + if (addr) {
> + struct page *page = pfn_to_page(addr >> PAGE_SHIFT);
> +
> + if (PageReserved(page)) {
> + /* allocated from bootmem */
> + if (page_size < PAGE_SIZE) {
> + /*
> + * this shouldn't happen, but if it is
> + * the case, leave the memory there
> + */
> + WARN_ON_ONCE(1);
> + } else {
> + unsigned int nr_pages =
> + 1 << get_order(page_size);
> + while (nr_pages--)
> + free_reserved_page(page++);
> + }
> + } else
> + free_pages((unsigned long)(__va(addr)),
> + get_order(page_size));
> +
> + vmemmap_remove_mapping(start, page_size);
> + }
> }
> -
> - return 0;
> -}
> -
> -void vmemmap_free(unsigned long start, unsigned long end)
> -{
> }
> -
> +#endif
> void register_page_bootmem_memmap(unsigned long section_nr,
> struct page *start_page, unsigned long size)
> {
>
^ permalink raw reply
* Re: [PATCH 4/4] powerpc: start loop at section start of start in vmemmap_populated()
From: Nathan Fontenot @ 2014-07-24 15:13 UTC (permalink / raw)
To: Li Zhong, linuxppc-dev; +Cc: paulus
In-Reply-To: <1402475019-19699-4-git-send-email-zhong@linux.vnet.ibm.com>
On 06/11/2014 03:23 AM, Li Zhong wrote:
> vmemmap_populated() checks whether the [start, start + page_size) has valid
> pfn numbers, to know whether a vmemmap mapping has been created that includes
> this range.
>
> Some range before end might not be checked by this loop:
> sec11start......start11..sec11end/sec12start..end....start12..sec12end
> as the above, for start11(section 11), it checks [sec11start, sec11end), and
> loop ends as the next start(start12) is bigger than end. However,
> [sec11end/sec12start, end) is not checked here.
>
> So before the loop, adjust the start to be the start of the section, so we don't miss ranges like the above.
>
> After we adjust start to be the start of the section, it also means it's
> aligned with vmemmap as of the sizeof struct page, so we could use
> page_to_pfn directly in the loop.
>
> Signed-off-by: Li Zhong <zhong@linux.vnet.ibm.com>
> Cc: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Acked-by: Nathan Fontenot <nfont@linux.vnet.ibm.com>
> ---
> arch/powerpc/mm/init_64.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c
> index 4963790..253b4b9 100644
> --- a/arch/powerpc/mm/init_64.c
> +++ b/arch/powerpc/mm/init_64.c
> @@ -175,9 +175,10 @@ static unsigned long __meminit vmemmap_section_start(unsigned long page)
> static int __meminit vmemmap_populated(unsigned long start, int page_size)
> {
> unsigned long end = start + page_size;
> + start = (unsigned long)(pfn_to_page(vmemmap_section_start(start)));
>
> for (; start < end; start += (PAGES_PER_SECTION * sizeof(struct page)))
> - if (pfn_valid(vmemmap_section_start(start)))
> + if (pfn_valid(page_to_pfn((struct page *)start)))
> return 1;
>
> return 0;
>
^ permalink raw reply
* Re: [PATCH v2 2/2] ASoC: fsl_asrc: Add ASRC ASoC CPU DAI and platform drivers
From: Tobias Klauser @ 2014-07-24 15:20 UTC (permalink / raw)
To: Nicolin Chen
Cc: mark.rutland, devicetree, alsa-devel, b42378, timur, pawel.moll,
ijc+devicetree, tiwai, linux-kernel, rdunlap, b02247, lgirdwood,
robh+dt, perex, broonie, galak, grant.likely, shawn.guo,
linuxppc-dev
In-Reply-To: <2c74a11776ffcc017c80dc31be4a8313755be8f5.1406189745.git.nicoleotsuka@gmail.com>
On 2014-07-24 at 10:35:29 +0200, Nicolin Chen <nicoleotsuka@gmail.com> wrote:
> The Asynchronous Sample Rate Converter (ASRC) converts the sampling rate of a
> signal associated with an input clock into a signal associated with a different
> output clock. The driver currently works as a Front End of DPCM with other Back
> Ends DAI links such as ESAI<->CS42888 and SSI<->WM8962 and SAI. It converts the
> original sample rate to a common rate supported by Back Ends for playback while
> converts the common rate of Back Ends to a desired rate for capture. It has 3
> pairs to support three different substreams within totally 10 channels.
>
> Signed-off-by: Nicolin Chen <nicoleotsuka@gmail.com>
> ---
[...]
> --- /dev/null
> +++ b/sound/soc/fsl/fsl_asrc.c
> @@ -0,0 +1,1031 @@
[...]
> +static struct platform_driver fsl_asrc_driver = {
> + .probe = fsl_asrc_probe,
> + .driver = {
> + .name = "fsl-asrc",
> + .owner = THIS_MODULE,
Not necessary, this will be set by module_platform_driver (or
the expansion of platform_driver_register, respectively).
> + .of_match_table = fsl_asrc_ids,
> + .pm = &fsl_asrc_pm,
> + },
> +};
> +module_platform_driver(fsl_asrc_driver);
[...]
> --- /dev/null
> +++ b/sound/soc/fsl/fsl_asrc_dma.c
> @@ -0,0 +1,384 @@
[...]
> +static int fsl_asrc_dma_pcm_new(struct snd_soc_pcm_runtime *rtd)
> +{
> + struct snd_card *card = rtd->card->snd_card;
> + struct snd_pcm_substream *substream;
> + struct snd_pcm *pcm = rtd->pcm;
> + u64 dma_mask = DMA_BIT_MASK(32);
> + int ret, i;
> +
> + if (!card->dev->dma_mask)
> + card->dev->dma_mask = &dma_mask;
dma_mask will go out of scope after fsl_asrc_dma_pcm_new returns,
resulting in a stray pointer in card->dev->dma_mask.
> + if (!card->dev->coherent_dma_mask)
> + card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
> +
> + for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) {
> + substream = pcm->streams[i].substream;
> +
> + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev,
> + FSL_ASRC_DMABUF_SIZE, &substream->dma_buffer);
> + if (ret) {
> + dev_err(card->dev, "failed to allocate DMA buffer\n");
> + goto err;
> + }
> + }
> +
> + return 0;
> +
> +err:
> + if (--i == 0)
> + snd_dma_free_pages(&pcm->streams[i].substream->dma_buffer);
> +
> + return ret;
> +}
> +
> +static void fsl_asrc_dma_pcm_free(struct snd_pcm *pcm)
> +{
> + struct snd_pcm_substream *substream;
> + int i;
> +
> + for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) {
> + substream = pcm->streams[i].substream;
> + if (!substream)
> + continue;
> +
> + snd_dma_free_pages(&substream->dma_buffer);
> + substream->dma_buffer.area = NULL;
> + substream->dma_buffer.addr = 0;
> + }
> +}
> +
> +struct snd_soc_platform_driver fsl_asrc_platform = {
> + .ops = &fsl_asrc_dma_pcm_ops,
> + .pcm_new = fsl_asrc_dma_pcm_new,
> + .pcm_free = fsl_asrc_dma_pcm_free,
> +};
> +EXPORT_SYMBOL_GPL(fsl_asrc_platform);
> --
> 1.8.4
^ permalink raw reply
* (request for -mm inclusion) Re: [PATCH v3] arm64, ia64, ppc, s390, sh, tile, um, x86, mm: Remove default gate area
From: Andy Lutomirski @ 2014-07-24 18:48 UTC (permalink / raw)
To: Richard Weinberger, Andrew Morton
Cc: linux-ia64@vger.kernel.org, linux-sh@vger.kernel.org,
Catalin Marinas, Heiko Carstens, linux-mm@kvack.org,
Paul Mackerras, H. Peter Anvin, linux-arch,
linux-s390@vger.kernel.org, X86 ML, Ingo Molnar, Fenghua Yu,
user-mode-linux-devel@lists.sourceforge.net, Will Deacon,
Jeff Dike, Chris Metcalf, Thomas Gleixner,
linux-arm-kernel@lists.infradead.org, Tony Luck, Nathan Lynch,
linux-kernel@vger.kernel.org, Martin Schwidefsky,
linux390@de.ibm.com, linuxppc-dev
[-- Attachment #1: Type: text/plain, Size: 2534 bytes --]
Hi akpm-
One more try before I spam the world with a new thread. Is this patch
okay for -mm?
So far, it's accumulated:
Acked-by: Nathan Lynch <nathan_lynch@mentor.com>
Acked-by: H. Peter Anvin <hpa@linux.intel.com>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> [in principle]
Acked-by: Richard Weinberger <richard@nod.at> [for um]
Acked-by: Will Deacon <will.deacon@arm.com> [for arm64]
For convenience, I've attached the patch w/ the acked-by's folded in
and with no other changes.
Thanks,
Andy
On Fri, Jul 18, 2014 at 9:53 AM, Andy Lutomirski <luto@amacapital.net> wrote:
>
> On Jul 18, 2014 3:20 AM, "Richard Weinberger" <richard@nod.at> wrote:
>>
>> Am 18.07.2014 12:14, schrieb Will Deacon:
>> > On Tue, Jul 15, 2014 at 03:47:26PM +0100, Andy Lutomirski wrote:
>> >> On Sun, Jul 13, 2014 at 1:01 PM, Andy Lutomirski <luto@amacapital.net>
>> >> wrote:
>> >>> The core mm code will provide a default gate area based on
>> >>> FIXADDR_USER_START and FIXADDR_USER_END if
>> >>> !defined(__HAVE_ARCH_GATE_AREA) && defined(AT_SYSINFO_EHDR).
>> >>>
>> >>> This default is only useful for ia64. arm64, ppc, s390, sh, tile,
>> >>> 64-bit UML, and x86_32 have their own code just to disable it. arm,
>> >>> 32-bit UML, and x86_64 have gate areas, but they have their own
>> >>> implementations.
>> >>>
>> >>> This gets rid of the default and moves the code into ia64.
>> >>>
>> >>> This should save some code on architectures without a gate area: it's
>> >>> now possible to inline the gate_area functions in the default case.
>> >>
>> >> Can one of you pull this somewhere? Otherwise I can put it somewhere
>> >> stable and ask for -next inclusion, but that seems like overkill for a
>> >> single patch.
>>
>> For the um bits:
>> Acked-by: Richard Weinberger <richard@nod.at>
>>
>> > I'd be happy to take the arm64 part, but it doesn't feel right for mm/*
>> > changes (or changes to other archs) to go via our tree.
>> >
>> > I'm not sure what the best approach is if you want to send this via a
>> > single
>> > tree. Maybe you could ask akpm nicely?
>>
>> Going though Andrew's tree sounds sane to me.
>
> Splitting this will be annoying: I'd probably have to add a flag asking for
> the new behavior, update all the arches, then remove the flag. The chance
> of screwing up bisectability in the process seems pretty high. This seems
> like overkill for a patch that mostly deletes code.
>
> Akpm, can you take this?
>
> --Andy
>
>>
>> Thanks,
>> //richard
--
Andy Lutomirski
AMA Capital Management, LLC
[-- Attachment #2: 0001-arm64-ia64-ppc-s390-sh-tile-um-x86-mm-Remove-default.patch --]
[-- Type: text/x-diff, Size: 14779 bytes --]
From 3a4ddfaab96d1dd06b9cd6298e74a91c5a956ece Mon Sep 17 00:00:00 2001
Message-Id: <3a4ddfaab96d1dd06b9cd6298e74a91c5a956ece.1406227593.git.luto@amacapital.net>
From: Andy Lutomirski <luto@amacapital.net>
Date: Tue, 17 Jun 2014 10:28:20 -0700
Subject: [PATCH] arm64,ia64,ppc,s390,sh,tile,um,x86,mm: Remove default gate
area
The core mm code will provide a default gate area based on
FIXADDR_USER_START and FIXADDR_USER_END if
!defined(__HAVE_ARCH_GATE_AREA) && defined(AT_SYSINFO_EHDR).
This default is only useful for ia64. arm64, ppc, s390, sh, tile,
64-bit UML, and x86_32 have their own code just to disable it. arm,
32-bit UML, and x86_64 have gate areas, but they have their own
implementations.
This gets rid of the default and moves the code into ia64.
This should save some code on architectures without a gate area: it's
now possible to inline the gate_area functions in the default case.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Fenghua Yu <fenghua.yu@intel.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: linux390@de.ibm.com
Cc: Chris Metcalf <cmetcalf@tilera.com>
Cc: Jeff Dike <jdike@addtoit.com>
Cc: Richard Weinberger <richard@nod.at>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Nathan Lynch <Nathan_Lynch@mentor.com>
Cc: x86@kernel.org
Cc: linux-arch@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-ia64@vger.kernel.org
Cc: linuxppc-dev@lists.ozlabs.org
Cc: linux-s390@vger.kernel.org
Cc: linux-sh@vger.kernel.org
Cc: user-mode-linux-devel@lists.sourceforge.net
Cc: linux-mm@kvack.org
Acked-by: Nathan Lynch <nathan_lynch@mentor.com>
Acked-by: H. Peter Anvin <hpa@linux.intel.com>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> [in principle]
Acked-by: Richard Weinberger <richard@nod.at> [for um]
Acked-by: Will Deacon <will.deacon@arm.com> [for arm64]
Signed-off-by: Andy Lutomirski <luto@amacapital.net>
---
arch/arm64/include/asm/page.h | 3 ---
arch/arm64/kernel/vdso.c | 19 -------------------
arch/ia64/include/asm/page.h | 2 ++
arch/ia64/mm/init.c | 26 ++++++++++++++++++++++++++
arch/powerpc/include/asm/page.h | 3 ---
arch/powerpc/kernel/vdso.c | 16 ----------------
arch/s390/include/asm/page.h | 2 --
arch/s390/kernel/vdso.c | 15 ---------------
arch/sh/include/asm/page.h | 5 -----
arch/sh/kernel/vsyscall/vsyscall.c | 15 ---------------
arch/tile/include/asm/page.h | 6 ------
arch/tile/kernel/vdso.c | 15 ---------------
arch/um/include/asm/page.h | 5 +++++
arch/x86/include/asm/page.h | 1 -
arch/x86/include/asm/page_64.h | 2 ++
arch/x86/um/asm/elf.h | 1 -
arch/x86/um/mem_64.c | 15 ---------------
arch/x86/vdso/vdso32-setup.c | 19 +------------------
include/linux/mm.h | 17 ++++++++++++-----
mm/memory.c | 38 --------------------------------------
mm/nommu.c | 5 -----
21 files changed, 48 insertions(+), 182 deletions(-)
diff --git a/arch/arm64/include/asm/page.h b/arch/arm64/include/asm/page.h
index 46bf666..992710f 100644
--- a/arch/arm64/include/asm/page.h
+++ b/arch/arm64/include/asm/page.h
@@ -28,9 +28,6 @@
#define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT)
#define PAGE_MASK (~(PAGE_SIZE-1))
-/* We do define AT_SYSINFO_EHDR but don't use the gate mechanism */
-#define __HAVE_ARCH_GATE_AREA 1
-
#ifndef __ASSEMBLY__
#ifdef CONFIG_ARM64_64K_PAGES
diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c
index 50384fe..f630626 100644
--- a/arch/arm64/kernel/vdso.c
+++ b/arch/arm64/kernel/vdso.c
@@ -187,25 +187,6 @@ const char *arch_vma_name(struct vm_area_struct *vma)
}
/*
- * We define AT_SYSINFO_EHDR, so we need these function stubs to keep
- * Linux happy.
- */
-int in_gate_area_no_mm(unsigned long addr)
-{
- return 0;
-}
-
-int in_gate_area(struct mm_struct *mm, unsigned long addr)
-{
- return 0;
-}
-
-struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
-{
- return NULL;
-}
-
-/*
* Update the vDSO data page to keep in sync with kernel timekeeping.
*/
void update_vsyscall(struct timekeeper *tk)
diff --git a/arch/ia64/include/asm/page.h b/arch/ia64/include/asm/page.h
index f1e1b2e..1f1bf14 100644
--- a/arch/ia64/include/asm/page.h
+++ b/arch/ia64/include/asm/page.h
@@ -231,4 +231,6 @@ get_order (unsigned long size)
#define PERCPU_ADDR (-PERCPU_PAGE_SIZE)
#define LOAD_OFFSET (KERNEL_START - KERNEL_TR_PAGE_SIZE)
+#define __HAVE_ARCH_GATE_AREA 1
+
#endif /* _ASM_IA64_PAGE_H */
diff --git a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c
index 25c3502..35efaa3 100644
--- a/arch/ia64/mm/init.c
+++ b/arch/ia64/mm/init.c
@@ -278,6 +278,32 @@ setup_gate (void)
ia64_patch_gate();
}
+static struct vm_area_struct gate_vma;
+
+static int __init gate_vma_init(void)
+{
+ gate_vma.vm_mm = NULL;
+ gate_vma.vm_start = FIXADDR_USER_START;
+ gate_vma.vm_end = FIXADDR_USER_END;
+ gate_vma.vm_flags = VM_READ | VM_MAYREAD | VM_EXEC | VM_MAYEXEC;
+ gate_vma.vm_page_prot = __P101;
+
+ return 0;
+}
+__initcall(gate_vma_init);
+
+struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
+{
+ return &gate_vma;
+}
+
+int in_gate_area_no_mm(unsigned long addr)
+{
+ if ((addr >= FIXADDR_USER_START) && (addr < FIXADDR_USER_END))
+ return 1;
+ return 0;
+}
+
void ia64_mmu_init(void *my_cpu_data)
{
unsigned long pta, impl_va_bits;
diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h
index 32e4e21..26fe1ae 100644
--- a/arch/powerpc/include/asm/page.h
+++ b/arch/powerpc/include/asm/page.h
@@ -48,9 +48,6 @@ extern unsigned int HPAGE_SHIFT;
#define HUGE_MAX_HSTATE (MMU_PAGE_COUNT-1)
#endif
-/* We do define AT_SYSINFO_EHDR but don't use the gate mechanism */
-#define __HAVE_ARCH_GATE_AREA 1
-
/*
* Subtle: (1 << PAGE_SHIFT) is an int, not an unsigned long. So if we
* assign PAGE_MASK to a larger type it gets extended the way we want
diff --git a/arch/powerpc/kernel/vdso.c b/arch/powerpc/kernel/vdso.c
index ce74c33..f174351 100644
--- a/arch/powerpc/kernel/vdso.c
+++ b/arch/powerpc/kernel/vdso.c
@@ -840,19 +840,3 @@ static int __init vdso_init(void)
return 0;
}
arch_initcall(vdso_init);
-
-int in_gate_area_no_mm(unsigned long addr)
-{
- return 0;
-}
-
-int in_gate_area(struct mm_struct *mm, unsigned long addr)
-{
- return 0;
-}
-
-struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
-{
- return NULL;
-}
-
diff --git a/arch/s390/include/asm/page.h b/arch/s390/include/asm/page.h
index 114258e..7b2ac6e 100644
--- a/arch/s390/include/asm/page.h
+++ b/arch/s390/include/asm/page.h
@@ -162,6 +162,4 @@ static inline int devmem_is_allowed(unsigned long pfn)
#include <asm-generic/memory_model.h>
#include <asm-generic/getorder.h>
-#define __HAVE_ARCH_GATE_AREA 1
-
#endif /* _S390_PAGE_H */
diff --git a/arch/s390/kernel/vdso.c b/arch/s390/kernel/vdso.c
index 6136490..0bbb7e0 100644
--- a/arch/s390/kernel/vdso.c
+++ b/arch/s390/kernel/vdso.c
@@ -316,18 +316,3 @@ static int __init vdso_init(void)
return 0;
}
early_initcall(vdso_init);
-
-int in_gate_area_no_mm(unsigned long addr)
-{
- return 0;
-}
-
-int in_gate_area(struct mm_struct *mm, unsigned long addr)
-{
- return 0;
-}
-
-struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
-{
- return NULL;
-}
diff --git a/arch/sh/include/asm/page.h b/arch/sh/include/asm/page.h
index 15d9703..fe20d14 100644
--- a/arch/sh/include/asm/page.h
+++ b/arch/sh/include/asm/page.h
@@ -186,11 +186,6 @@ typedef struct page *pgtable_t;
#include <asm-generic/memory_model.h>
#include <asm-generic/getorder.h>
-/* vDSO support */
-#ifdef CONFIG_VSYSCALL
-#define __HAVE_ARCH_GATE_AREA
-#endif
-
/*
* Some drivers need to perform DMA into kmalloc'ed buffers
* and so we have to increase the kmalloc minalign for this.
diff --git a/arch/sh/kernel/vsyscall/vsyscall.c b/arch/sh/kernel/vsyscall/vsyscall.c
index 5ca5797..ea2aa13 100644
--- a/arch/sh/kernel/vsyscall/vsyscall.c
+++ b/arch/sh/kernel/vsyscall/vsyscall.c
@@ -92,18 +92,3 @@ const char *arch_vma_name(struct vm_area_struct *vma)
return NULL;
}
-
-struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
-{
- return NULL;
-}
-
-int in_gate_area(struct mm_struct *mm, unsigned long address)
-{
- return 0;
-}
-
-int in_gate_area_no_mm(unsigned long address)
-{
- return 0;
-}
diff --git a/arch/tile/include/asm/page.h b/arch/tile/include/asm/page.h
index 6727680..a213a8d 100644
--- a/arch/tile/include/asm/page.h
+++ b/arch/tile/include/asm/page.h
@@ -39,12 +39,6 @@
#define HPAGE_MASK (~(HPAGE_SIZE - 1))
/*
- * We do define AT_SYSINFO_EHDR to support vDSO,
- * but don't use the gate mechanism.
- */
-#define __HAVE_ARCH_GATE_AREA 1
-
-/*
* If the Kconfig doesn't specify, set a maximum zone order that
* is enough so that we can create huge pages from small pages given
* the respective sizes of the two page types. See <linux/mmzone.h>.
diff --git a/arch/tile/kernel/vdso.c b/arch/tile/kernel/vdso.c
index 1533af2..5bc51d7 100644
--- a/arch/tile/kernel/vdso.c
+++ b/arch/tile/kernel/vdso.c
@@ -121,21 +121,6 @@ const char *arch_vma_name(struct vm_area_struct *vma)
return NULL;
}
-struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
-{
- return NULL;
-}
-
-int in_gate_area(struct mm_struct *mm, unsigned long address)
-{
- return 0;
-}
-
-int in_gate_area_no_mm(unsigned long address)
-{
- return 0;
-}
-
int setup_vdso_pages(void)
{
struct page **pagelist;
diff --git a/arch/um/include/asm/page.h b/arch/um/include/asm/page.h
index 5ff53d9..71c5d13 100644
--- a/arch/um/include/asm/page.h
+++ b/arch/um/include/asm/page.h
@@ -119,4 +119,9 @@ extern unsigned long uml_physmem;
#include <asm-generic/getorder.h>
#endif /* __ASSEMBLY__ */
+
+#ifdef CONFIG_X86_32
+#define __HAVE_ARCH_GATE_AREA 1
+#endif
+
#endif /* __UM_PAGE_H */
diff --git a/arch/x86/include/asm/page.h b/arch/x86/include/asm/page.h
index 775873d..802dde3 100644
--- a/arch/x86/include/asm/page.h
+++ b/arch/x86/include/asm/page.h
@@ -70,7 +70,6 @@ extern bool __virt_addr_valid(unsigned long kaddr);
#include <asm-generic/memory_model.h>
#include <asm-generic/getorder.h>
-#define __HAVE_ARCH_GATE_AREA 1
#define HAVE_ARCH_HUGETLB_UNMAPPED_AREA
#endif /* __KERNEL__ */
diff --git a/arch/x86/include/asm/page_64.h b/arch/x86/include/asm/page_64.h
index 0f1ddee..f408caf 100644
--- a/arch/x86/include/asm/page_64.h
+++ b/arch/x86/include/asm/page_64.h
@@ -39,4 +39,6 @@ void copy_page(void *to, void *from);
#endif /* !__ASSEMBLY__ */
+#define __HAVE_ARCH_GATE_AREA 1
+
#endif /* _ASM_X86_PAGE_64_H */
diff --git a/arch/x86/um/asm/elf.h b/arch/x86/um/asm/elf.h
index 0feee2f..25a1022 100644
--- a/arch/x86/um/asm/elf.h
+++ b/arch/x86/um/asm/elf.h
@@ -216,6 +216,5 @@ extern long elf_aux_hwcap;
#define ELF_HWCAP (elf_aux_hwcap)
#define SET_PERSONALITY(ex) do ; while(0)
-#define __HAVE_ARCH_GATE_AREA 1
#endif
diff --git a/arch/x86/um/mem_64.c b/arch/x86/um/mem_64.c
index c6492e7..f8fecad 100644
--- a/arch/x86/um/mem_64.c
+++ b/arch/x86/um/mem_64.c
@@ -9,18 +9,3 @@ const char *arch_vma_name(struct vm_area_struct *vma)
return NULL;
}
-
-struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
-{
- return NULL;
-}
-
-int in_gate_area(struct mm_struct *mm, unsigned long addr)
-{
- return 0;
-}
-
-int in_gate_area_no_mm(unsigned long addr)
-{
- return 0;
-}
diff --git a/arch/x86/vdso/vdso32-setup.c b/arch/x86/vdso/vdso32-setup.c
index e4f7781..e904c27 100644
--- a/arch/x86/vdso/vdso32-setup.c
+++ b/arch/x86/vdso/vdso32-setup.c
@@ -115,23 +115,6 @@ static __init int ia32_binfmt_init(void)
return 0;
}
__initcall(ia32_binfmt_init);
-#endif
-
-#else /* CONFIG_X86_32 */
-
-struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
-{
- return NULL;
-}
-
-int in_gate_area(struct mm_struct *mm, unsigned long addr)
-{
- return 0;
-}
-
-int in_gate_area_no_mm(unsigned long addr)
-{
- return 0;
-}
+#endif /* CONFIG_SYSCTL */
#endif /* CONFIG_X86_64 */
diff --git a/include/linux/mm.h b/include/linux/mm.h
index e03dd29..8981cc8 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2014,13 +2014,20 @@ static inline bool kernel_page_present(struct page *page) { return true; }
#endif /* CONFIG_HIBERNATION */
#endif
+#ifdef __HAVE_ARCH_GATE_AREA
extern struct vm_area_struct *get_gate_vma(struct mm_struct *mm);
-#ifdef __HAVE_ARCH_GATE_AREA
-int in_gate_area_no_mm(unsigned long addr);
-int in_gate_area(struct mm_struct *mm, unsigned long addr);
+extern int in_gate_area_no_mm(unsigned long addr);
+extern int in_gate_area(struct mm_struct *mm, unsigned long addr);
#else
-int in_gate_area_no_mm(unsigned long addr);
-#define in_gate_area(mm, addr) ({(void)mm; in_gate_area_no_mm(addr);})
+static inline struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
+{
+ return NULL;
+}
+static inline int in_gate_area_no_mm(unsigned long addr) { return 0; }
+static inline int in_gate_area(struct mm_struct *mm, unsigned long addr)
+{
+ return 0;
+}
#endif /* __HAVE_ARCH_GATE_AREA */
#ifdef CONFIG_SYSCTL
diff --git a/mm/memory.c b/mm/memory.c
index d67fd9f..099d234 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -3399,44 +3399,6 @@ int __pmd_alloc(struct mm_struct *mm, pud_t *pud, unsigned long address)
}
#endif /* __PAGETABLE_PMD_FOLDED */
-#if !defined(__HAVE_ARCH_GATE_AREA)
-
-#if defined(AT_SYSINFO_EHDR)
-static struct vm_area_struct gate_vma;
-
-static int __init gate_vma_init(void)
-{
- gate_vma.vm_mm = NULL;
- gate_vma.vm_start = FIXADDR_USER_START;
- gate_vma.vm_end = FIXADDR_USER_END;
- gate_vma.vm_flags = VM_READ | VM_MAYREAD | VM_EXEC | VM_MAYEXEC;
- gate_vma.vm_page_prot = __P101;
-
- return 0;
-}
-__initcall(gate_vma_init);
-#endif
-
-struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
-{
-#ifdef AT_SYSINFO_EHDR
- return &gate_vma;
-#else
- return NULL;
-#endif
-}
-
-int in_gate_area_no_mm(unsigned long addr)
-{
-#ifdef AT_SYSINFO_EHDR
- if ((addr >= FIXADDR_USER_START) && (addr < FIXADDR_USER_END))
- return 1;
-#endif
- return 0;
-}
-
-#endif /* __HAVE_ARCH_GATE_AREA */
-
static int __follow_pte(struct mm_struct *mm, unsigned long address,
pte_t **ptepp, spinlock_t **ptlp)
{
diff --git a/mm/nommu.c b/mm/nommu.c
index 4a852f6..a881d96 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -1981,11 +1981,6 @@ error:
return -ENOMEM;
}
-int in_gate_area_no_mm(unsigned long addr)
-{
- return 0;
-}
-
int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
BUG();
--
1.9.3
^ permalink raw reply related
* [PATCH v4] arm64, ia64, ppc, s390, sh, tile, um, x86, mm: Remove default gate area
From: Andy Lutomirski @ 2014-07-24 20:56 UTC (permalink / raw)
To: Andrew Morton
Cc: linux-ia64, linux-sh, Catalin Marinas, Heiko Carstens, linux-mm,
Paul Mackerras, H. Peter Anvin, linux-arch, linux-s390,
Richard Weinberger, x86, Ingo Molnar, Fenghua Yu,
user-mode-linux-devel, Will Deacon, Jeff Dike, Chris Metcalf,
Thomas Gleixner, linux-arm-kernel, Tony Luck, Nathan Lynch,
linux-kernel, Andy Lutomirski, Martin Schwidefsky, linux390,
linuxppc-dev
The core mm code will provide a default gate area based on
FIXADDR_USER_START and FIXADDR_USER_END if
!defined(__HAVE_ARCH_GATE_AREA) && defined(AT_SYSINFO_EHDR).
This default is only useful for ia64. arm64, ppc, s390, sh, tile,
64-bit UML, and x86_32 have their own code just to disable it. arm,
32-bit UML, and x86_64 have gate areas, but they have their own
implementations.
This gets rid of the default and moves the code into ia64.
This should save some code on architectures without a gate area: it's
now possible to inline the gate_area functions in the default case.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Fenghua Yu <fenghua.yu@intel.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: linux390@de.ibm.com
Cc: Chris Metcalf <cmetcalf@tilera.com>
Cc: Jeff Dike <jdike@addtoit.com>
Cc: Richard Weinberger <richard@nod.at>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Nathan Lynch <Nathan_Lynch@mentor.com>
Cc: x86@kernel.org
Cc: linux-arch@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-ia64@vger.kernel.org
Cc: linuxppc-dev@lists.ozlabs.org
Cc: linux-s390@vger.kernel.org
Cc: linux-sh@vger.kernel.org
Cc: user-mode-linux-devel@lists.sourceforge.net
Cc: linux-mm@kvack.org
Acked-by: Nathan Lynch <nathan_lynch@mentor.com>
Acked-by: H. Peter Anvin <hpa@linux.intel.com>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> [in principle]
Acked-by: Richard Weinberger <richard@nod.at> [for um]
Acked-by: Will Deacon <will.deacon@arm.com> [for arm64]
Signed-off-by: Andy Lutomirski <luto@amacapital.net>
---
Changes from v3: Fix ia64 build (I was missing in_gate_area in the ia64 part).
This now builds successfully with allnoconfig on ia64. I don't know how
to test-boot it (no QEMU support), and I got a failure on a randconfig,
but I don't think the randconfig failure has anything to do with this
patch.
arch/arm64/include/asm/page.h | 3 ---
arch/arm64/kernel/vdso.c | 19 -------------------
arch/ia64/include/asm/page.h | 2 ++
arch/ia64/mm/init.c | 31 +++++++++++++++++++++++++++++++
arch/powerpc/include/asm/page.h | 3 ---
arch/powerpc/kernel/vdso.c | 16 ----------------
arch/s390/include/asm/page.h | 2 --
arch/s390/kernel/vdso.c | 15 ---------------
arch/sh/include/asm/page.h | 5 -----
arch/sh/kernel/vsyscall/vsyscall.c | 15 ---------------
arch/tile/include/asm/page.h | 6 ------
arch/tile/kernel/vdso.c | 15 ---------------
arch/um/include/asm/page.h | 5 +++++
arch/x86/include/asm/page.h | 1 -
arch/x86/include/asm/page_64.h | 2 ++
arch/x86/um/asm/elf.h | 1 -
arch/x86/um/mem_64.c | 15 ---------------
arch/x86/vdso/vdso32-setup.c | 19 +------------------
include/linux/mm.h | 17 ++++++++++++-----
mm/memory.c | 38 --------------------------------------
mm/nommu.c | 5 -----
21 files changed, 53 insertions(+), 182 deletions(-)
diff --git a/arch/arm64/include/asm/page.h b/arch/arm64/include/asm/page.h
index 46bf666..992710f 100644
--- a/arch/arm64/include/asm/page.h
+++ b/arch/arm64/include/asm/page.h
@@ -28,9 +28,6 @@
#define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT)
#define PAGE_MASK (~(PAGE_SIZE-1))
-/* We do define AT_SYSINFO_EHDR but don't use the gate mechanism */
-#define __HAVE_ARCH_GATE_AREA 1
-
#ifndef __ASSEMBLY__
#ifdef CONFIG_ARM64_64K_PAGES
diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c
index 50384fe..f630626 100644
--- a/arch/arm64/kernel/vdso.c
+++ b/arch/arm64/kernel/vdso.c
@@ -187,25 +187,6 @@ const char *arch_vma_name(struct vm_area_struct *vma)
}
/*
- * We define AT_SYSINFO_EHDR, so we need these function stubs to keep
- * Linux happy.
- */
-int in_gate_area_no_mm(unsigned long addr)
-{
- return 0;
-}
-
-int in_gate_area(struct mm_struct *mm, unsigned long addr)
-{
- return 0;
-}
-
-struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
-{
- return NULL;
-}
-
-/*
* Update the vDSO data page to keep in sync with kernel timekeeping.
*/
void update_vsyscall(struct timekeeper *tk)
diff --git a/arch/ia64/include/asm/page.h b/arch/ia64/include/asm/page.h
index f1e1b2e..1f1bf14 100644
--- a/arch/ia64/include/asm/page.h
+++ b/arch/ia64/include/asm/page.h
@@ -231,4 +231,6 @@ get_order (unsigned long size)
#define PERCPU_ADDR (-PERCPU_PAGE_SIZE)
#define LOAD_OFFSET (KERNEL_START - KERNEL_TR_PAGE_SIZE)
+#define __HAVE_ARCH_GATE_AREA 1
+
#endif /* _ASM_IA64_PAGE_H */
diff --git a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c
index 25c3502..0e7ad82 100644
--- a/arch/ia64/mm/init.c
+++ b/arch/ia64/mm/init.c
@@ -278,6 +278,37 @@ setup_gate (void)
ia64_patch_gate();
}
+static struct vm_area_struct gate_vma;
+
+static int __init gate_vma_init(void)
+{
+ gate_vma.vm_mm = NULL;
+ gate_vma.vm_start = FIXADDR_USER_START;
+ gate_vma.vm_end = FIXADDR_USER_END;
+ gate_vma.vm_flags = VM_READ | VM_MAYREAD | VM_EXEC | VM_MAYEXEC;
+ gate_vma.vm_page_prot = __P101;
+
+ return 0;
+}
+__initcall(gate_vma_init);
+
+struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
+{
+ return &gate_vma;
+}
+
+int in_gate_area_no_mm(unsigned long addr)
+{
+ if ((addr >= FIXADDR_USER_START) && (addr < FIXADDR_USER_END))
+ return 1;
+ return 0;
+}
+
+int in_gate_area(struct mm_struct *mm, unsigned long addr)
+{
+ return in_gate_area_no_mm(addr);
+}
+
void ia64_mmu_init(void *my_cpu_data)
{
unsigned long pta, impl_va_bits;
diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h
index 32e4e21..26fe1ae 100644
--- a/arch/powerpc/include/asm/page.h
+++ b/arch/powerpc/include/asm/page.h
@@ -48,9 +48,6 @@ extern unsigned int HPAGE_SHIFT;
#define HUGE_MAX_HSTATE (MMU_PAGE_COUNT-1)
#endif
-/* We do define AT_SYSINFO_EHDR but don't use the gate mechanism */
-#define __HAVE_ARCH_GATE_AREA 1
-
/*
* Subtle: (1 << PAGE_SHIFT) is an int, not an unsigned long. So if we
* assign PAGE_MASK to a larger type it gets extended the way we want
diff --git a/arch/powerpc/kernel/vdso.c b/arch/powerpc/kernel/vdso.c
index ce74c33..f174351 100644
--- a/arch/powerpc/kernel/vdso.c
+++ b/arch/powerpc/kernel/vdso.c
@@ -840,19 +840,3 @@ static int __init vdso_init(void)
return 0;
}
arch_initcall(vdso_init);
-
-int in_gate_area_no_mm(unsigned long addr)
-{
- return 0;
-}
-
-int in_gate_area(struct mm_struct *mm, unsigned long addr)
-{
- return 0;
-}
-
-struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
-{
- return NULL;
-}
-
diff --git a/arch/s390/include/asm/page.h b/arch/s390/include/asm/page.h
index 114258e..7b2ac6e 100644
--- a/arch/s390/include/asm/page.h
+++ b/arch/s390/include/asm/page.h
@@ -162,6 +162,4 @@ static inline int devmem_is_allowed(unsigned long pfn)
#include <asm-generic/memory_model.h>
#include <asm-generic/getorder.h>
-#define __HAVE_ARCH_GATE_AREA 1
-
#endif /* _S390_PAGE_H */
diff --git a/arch/s390/kernel/vdso.c b/arch/s390/kernel/vdso.c
index 6136490..0bbb7e0 100644
--- a/arch/s390/kernel/vdso.c
+++ b/arch/s390/kernel/vdso.c
@@ -316,18 +316,3 @@ static int __init vdso_init(void)
return 0;
}
early_initcall(vdso_init);
-
-int in_gate_area_no_mm(unsigned long addr)
-{
- return 0;
-}
-
-int in_gate_area(struct mm_struct *mm, unsigned long addr)
-{
- return 0;
-}
-
-struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
-{
- return NULL;
-}
diff --git a/arch/sh/include/asm/page.h b/arch/sh/include/asm/page.h
index 15d9703..fe20d14 100644
--- a/arch/sh/include/asm/page.h
+++ b/arch/sh/include/asm/page.h
@@ -186,11 +186,6 @@ typedef struct page *pgtable_t;
#include <asm-generic/memory_model.h>
#include <asm-generic/getorder.h>
-/* vDSO support */
-#ifdef CONFIG_VSYSCALL
-#define __HAVE_ARCH_GATE_AREA
-#endif
-
/*
* Some drivers need to perform DMA into kmalloc'ed buffers
* and so we have to increase the kmalloc minalign for this.
diff --git a/arch/sh/kernel/vsyscall/vsyscall.c b/arch/sh/kernel/vsyscall/vsyscall.c
index 5ca5797..ea2aa13 100644
--- a/arch/sh/kernel/vsyscall/vsyscall.c
+++ b/arch/sh/kernel/vsyscall/vsyscall.c
@@ -92,18 +92,3 @@ const char *arch_vma_name(struct vm_area_struct *vma)
return NULL;
}
-
-struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
-{
- return NULL;
-}
-
-int in_gate_area(struct mm_struct *mm, unsigned long address)
-{
- return 0;
-}
-
-int in_gate_area_no_mm(unsigned long address)
-{
- return 0;
-}
diff --git a/arch/tile/include/asm/page.h b/arch/tile/include/asm/page.h
index 6727680..a213a8d 100644
--- a/arch/tile/include/asm/page.h
+++ b/arch/tile/include/asm/page.h
@@ -39,12 +39,6 @@
#define HPAGE_MASK (~(HPAGE_SIZE - 1))
/*
- * We do define AT_SYSINFO_EHDR to support vDSO,
- * but don't use the gate mechanism.
- */
-#define __HAVE_ARCH_GATE_AREA 1
-
-/*
* If the Kconfig doesn't specify, set a maximum zone order that
* is enough so that we can create huge pages from small pages given
* the respective sizes of the two page types. See <linux/mmzone.h>.
diff --git a/arch/tile/kernel/vdso.c b/arch/tile/kernel/vdso.c
index 1533af2..5bc51d7 100644
--- a/arch/tile/kernel/vdso.c
+++ b/arch/tile/kernel/vdso.c
@@ -121,21 +121,6 @@ const char *arch_vma_name(struct vm_area_struct *vma)
return NULL;
}
-struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
-{
- return NULL;
-}
-
-int in_gate_area(struct mm_struct *mm, unsigned long address)
-{
- return 0;
-}
-
-int in_gate_area_no_mm(unsigned long address)
-{
- return 0;
-}
-
int setup_vdso_pages(void)
{
struct page **pagelist;
diff --git a/arch/um/include/asm/page.h b/arch/um/include/asm/page.h
index 5ff53d9..71c5d13 100644
--- a/arch/um/include/asm/page.h
+++ b/arch/um/include/asm/page.h
@@ -119,4 +119,9 @@ extern unsigned long uml_physmem;
#include <asm-generic/getorder.h>
#endif /* __ASSEMBLY__ */
+
+#ifdef CONFIG_X86_32
+#define __HAVE_ARCH_GATE_AREA 1
+#endif
+
#endif /* __UM_PAGE_H */
diff --git a/arch/x86/include/asm/page.h b/arch/x86/include/asm/page.h
index 775873d..802dde3 100644
--- a/arch/x86/include/asm/page.h
+++ b/arch/x86/include/asm/page.h
@@ -70,7 +70,6 @@ extern bool __virt_addr_valid(unsigned long kaddr);
#include <asm-generic/memory_model.h>
#include <asm-generic/getorder.h>
-#define __HAVE_ARCH_GATE_AREA 1
#define HAVE_ARCH_HUGETLB_UNMAPPED_AREA
#endif /* __KERNEL__ */
diff --git a/arch/x86/include/asm/page_64.h b/arch/x86/include/asm/page_64.h
index 0f1ddee..f408caf 100644
--- a/arch/x86/include/asm/page_64.h
+++ b/arch/x86/include/asm/page_64.h
@@ -39,4 +39,6 @@ void copy_page(void *to, void *from);
#endif /* !__ASSEMBLY__ */
+#define __HAVE_ARCH_GATE_AREA 1
+
#endif /* _ASM_X86_PAGE_64_H */
diff --git a/arch/x86/um/asm/elf.h b/arch/x86/um/asm/elf.h
index 0feee2f..25a1022 100644
--- a/arch/x86/um/asm/elf.h
+++ b/arch/x86/um/asm/elf.h
@@ -216,6 +216,5 @@ extern long elf_aux_hwcap;
#define ELF_HWCAP (elf_aux_hwcap)
#define SET_PERSONALITY(ex) do ; while(0)
-#define __HAVE_ARCH_GATE_AREA 1
#endif
diff --git a/arch/x86/um/mem_64.c b/arch/x86/um/mem_64.c
index c6492e7..f8fecad 100644
--- a/arch/x86/um/mem_64.c
+++ b/arch/x86/um/mem_64.c
@@ -9,18 +9,3 @@ const char *arch_vma_name(struct vm_area_struct *vma)
return NULL;
}
-
-struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
-{
- return NULL;
-}
-
-int in_gate_area(struct mm_struct *mm, unsigned long addr)
-{
- return 0;
-}
-
-int in_gate_area_no_mm(unsigned long addr)
-{
- return 0;
-}
diff --git a/arch/x86/vdso/vdso32-setup.c b/arch/x86/vdso/vdso32-setup.c
index e4f7781..e904c27 100644
--- a/arch/x86/vdso/vdso32-setup.c
+++ b/arch/x86/vdso/vdso32-setup.c
@@ -115,23 +115,6 @@ static __init int ia32_binfmt_init(void)
return 0;
}
__initcall(ia32_binfmt_init);
-#endif
-
-#else /* CONFIG_X86_32 */
-
-struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
-{
- return NULL;
-}
-
-int in_gate_area(struct mm_struct *mm, unsigned long addr)
-{
- return 0;
-}
-
-int in_gate_area_no_mm(unsigned long addr)
-{
- return 0;
-}
+#endif /* CONFIG_SYSCTL */
#endif /* CONFIG_X86_64 */
diff --git a/include/linux/mm.h b/include/linux/mm.h
index e03dd29..8981cc8 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2014,13 +2014,20 @@ static inline bool kernel_page_present(struct page *page) { return true; }
#endif /* CONFIG_HIBERNATION */
#endif
+#ifdef __HAVE_ARCH_GATE_AREA
extern struct vm_area_struct *get_gate_vma(struct mm_struct *mm);
-#ifdef __HAVE_ARCH_GATE_AREA
-int in_gate_area_no_mm(unsigned long addr);
-int in_gate_area(struct mm_struct *mm, unsigned long addr);
+extern int in_gate_area_no_mm(unsigned long addr);
+extern int in_gate_area(struct mm_struct *mm, unsigned long addr);
#else
-int in_gate_area_no_mm(unsigned long addr);
-#define in_gate_area(mm, addr) ({(void)mm; in_gate_area_no_mm(addr);})
+static inline struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
+{
+ return NULL;
+}
+static inline int in_gate_area_no_mm(unsigned long addr) { return 0; }
+static inline int in_gate_area(struct mm_struct *mm, unsigned long addr)
+{
+ return 0;
+}
#endif /* __HAVE_ARCH_GATE_AREA */
#ifdef CONFIG_SYSCTL
diff --git a/mm/memory.c b/mm/memory.c
index d67fd9f..099d234 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -3399,44 +3399,6 @@ int __pmd_alloc(struct mm_struct *mm, pud_t *pud, unsigned long address)
}
#endif /* __PAGETABLE_PMD_FOLDED */
-#if !defined(__HAVE_ARCH_GATE_AREA)
-
-#if defined(AT_SYSINFO_EHDR)
-static struct vm_area_struct gate_vma;
-
-static int __init gate_vma_init(void)
-{
- gate_vma.vm_mm = NULL;
- gate_vma.vm_start = FIXADDR_USER_START;
- gate_vma.vm_end = FIXADDR_USER_END;
- gate_vma.vm_flags = VM_READ | VM_MAYREAD | VM_EXEC | VM_MAYEXEC;
- gate_vma.vm_page_prot = __P101;
-
- return 0;
-}
-__initcall(gate_vma_init);
-#endif
-
-struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
-{
-#ifdef AT_SYSINFO_EHDR
- return &gate_vma;
-#else
- return NULL;
-#endif
-}
-
-int in_gate_area_no_mm(unsigned long addr)
-{
-#ifdef AT_SYSINFO_EHDR
- if ((addr >= FIXADDR_USER_START) && (addr < FIXADDR_USER_END))
- return 1;
-#endif
- return 0;
-}
-
-#endif /* __HAVE_ARCH_GATE_AREA */
-
static int __follow_pte(struct mm_struct *mm, unsigned long address,
pte_t **ptepp, spinlock_t **ptlp)
{
diff --git a/mm/nommu.c b/mm/nommu.c
index 4a852f6..a881d96 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -1981,11 +1981,6 @@ error:
return -ENOMEM;
}
-int in_gate_area_no_mm(unsigned long addr)
-{
- return 0;
-}
-
int filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
BUG();
--
1.9.3
^ permalink raw reply related
* Kernel crash caused by cpufreq
From: Gavin Shan @ 2014-07-25 1:07 UTC (permalink / raw)
To: linuxppc-dev; +Cc: gwshan
I'm tracing one LSI interrupt issue on P8 box, and eventually into the
following kernel crash. Not sure if there is one fix against this? :-)
Starting Linux PPC64 #401 SMP Fri Jul 25 10:52:28 EST 2014
-----------------------------------------------------
ppc64_pft_size = 0x0
physicalMemorySize = 0x800000000
htab_address = 0xc0000007fe000000
htab_hash_mask = 0x3ffff
-----------------------------------------------------
<- setup_system()
Initializing cgroup subsys cpuset
Initializing cgroup subsys cpuacct
Linux version 3.16.0-rc6-00076-g4226dbe-dirty (shangw@shangw) (gcc version 4.5.2 (crosstool-NG 1.19.0) ) #401 SMP Fri Jul 25 10:52:28 EST 2014
:
< Unrelated log stripped >
:
------------[ cut here ]------------
kernel BUG at drivers/cpufreq/powernv-cpufreq.c:134!
cpu 0x1: Vector: 700 (Program Check) at [c0000007f8483370]
pc: c00000000096f32c: .pstate_id_to_freq+0x2c/0x50
lr: c00000000096f37c: .powernv_read_cpu_freq+0x2c/0x50
sp: c0000007f84835f0
msr: 9000000000029032
current = 0xc0000007f8400000
paca = 0xc00000000ff00400 softe: 0 irq_happened: 0x01
pid = 1, comm = swapper/0
kernel BUG at drivers/cpufreq/powernv-cpufreq.c:134!
enter ? for help
[link register ] c00000000096f37c .powernv_read_cpu_freq+0x2c/0x50
[c0000007f84835f0] c000000001076070 key_type_dns_resolver+0xef20/0x40d20 (unreliable)
[c0000007f8483670] c00000000010812c .generic_exec_single+0x8c/0x270
[c0000007f8483730] c0000000001083d0 .smp_call_function_single+0x90/0xb0
[c0000007f84837b0] c000000000108cec .smp_call_function_any+0x15c/0x200
[c0000007f8483860] c00000000096f27c .powernv_cpufreq_get+0x3c/0x60
[c0000007f84838f0] c000000000969dc4 .__cpufreq_add_dev.clone.11+0x574/0xa20
[c0000007f84839e0] c0000000005f7a4c .subsys_interface_register+0xec/0x130
[c0000007f8483a90] c000000000967af8 .cpufreq_register_driver+0x168/0x2d0
[c0000007f8483b30] c000000000f774cc .powernv_cpufreq_init+0x210/0x244
[c0000007f8483be0] c00000000000bc08 .do_one_initcall+0xc8/0x240
[c0000007f8483ce0] c000000000f44054 .kernel_init_freeable+0x268/0x33c
[c0000007f8483db0] c00000000000c4dc .kernel_init+0x1c/0x110
[c0000007f8483e30] c00000000000a428 .ret_from_kernel_thread+0x58/0xb0
1:mon> e
cpu 0x1: Vector: 700 (Program Check) at [c0000007f8483370]
pc: c00000000096f32c: .pstate_id_to_freq+0x2c/0x50
lr: c00000000096f37c: .powernv_read_cpu_freq+0x2c/0x50
sp: c0000007f84835f0
msr: 9000000000029032
current = 0xc0000007f8400000
paca = 0xc00000000ff00400 softe: 0 irq_happened: 0x01
pid = 1, comm = swapper/0
kernel BUG at drivers/cpufreq/powernv-cpufreq.c:134!
1:mon> r
R00 = 0000000000000042 R16 = 0000000000000000
R01 = c0000007f84835f0 R17 = 0000000000000000
R02 = c00000000116c430 R18 = 0000000000000000
R03 = ffffffffffffffbe R19 = 0000000000000001
R04 = 0000000000000000 R20 = c00000078bd61e58
R05 = c00000000114ca78 R21 = c000000001008910
R06 = c0000007f84838d0 R22 = c0000000011c1b74
R07 = 0000000000000001 R23 = c000000002200030
R08 = c0000000011c1910 R24 = 0000000000000001
R09 = c000000001f2970c R25 = c000000001008730
R10 = 0000000000000001 R26 = c000000001f29478
R11 = 0000000000000042 R27 = c0000007f84838d0
R12 = 0000000044002084 R28 = c00000000114ca78
R13 = c00000000ff00400 R29 = 0000000000000000
R14 = c00000000000c4c0 R30 = c0000000010a90f0
R15 = 0000000000000000 R31 = c0000007f84838d0
pc = c00000000096f32c .pstate_id_to_freq+0x2c/0x50
cfar= c00000000096f324 .pstate_id_to_freq+0x24/0x50
lr = c00000000096f37c .powernv_read_cpu_freq+0x2c/0x50
msr = 9000000000029032 cr = 44002082
ctr = c00000000096f350 xer = 0000000020000000 trap = 700
1:mon>
Thanks,
Gavin
^ permalink raw reply
* Re: [PATCH v2 2/2] ASoC: fsl_asrc: Add ASRC ASoC CPU DAI and platform drivers
From: Nicolin Chen @ 2014-07-25 2:05 UTC (permalink / raw)
To: Tobias Klauser
Cc: mark.rutland, devicetree, alsa-devel, b42378, timur, pawel.moll,
ijc+devicetree, tiwai, linux-kernel, rdunlap, b02247, lgirdwood,
robh+dt, perex, Nicolin Chen, broonie, galak, grant.likely,
shawn.guo, linuxppc-dev
In-Reply-To: <20140724152045.GA3384@distanz.ch>
Hi Tobias,
On Thu, Jul 24, 2014 at 05:20:45PM +0200, Tobias Klauser wrote:
> On 2014-07-24 at 10:35:29 +0200, Nicolin Chen <nicoleotsuka@gmail.com> wrote:
> > The Asynchronous Sample Rate Converter (ASRC) converts the sampling rate of a
> > signal associated with an input clock into a signal associated with a different
> > output clock. The driver currently works as a Front End of DPCM with other Back
> > Ends DAI links such as ESAI<->CS42888 and SSI<->WM8962 and SAI. It converts the
> > original sample rate to a common rate supported by Back Ends for playback while
> > converts the common rate of Back Ends to a desired rate for capture. It has 3
> > pairs to support three different substreams within totally 10 channels.
> >
> > Signed-off-by: Nicolin Chen <nicoleotsuka@gmail.com>
> > ---
>
> [...]
>
> > --- /dev/null
> > +++ b/sound/soc/fsl/fsl_asrc.c
> > @@ -0,0 +1,1031 @@
>
> [...]
>
> > +static struct platform_driver fsl_asrc_driver = {
> > + .probe = fsl_asrc_probe,
> > + .driver = {
> > + .name = "fsl-asrc",
> > + .owner = THIS_MODULE,
>
> Not necessary, this will be set by module_platform_driver (or
> the expansion of platform_driver_register, respectively).
>
> > + .of_match_table = fsl_asrc_ids,
> > + .pm = &fsl_asrc_pm,
> > + },
> > +};
> > +module_platform_driver(fsl_asrc_driver);
>
> [...]
>
> > --- /dev/null
> > +++ b/sound/soc/fsl/fsl_asrc_dma.c
> > @@ -0,0 +1,384 @@
>
> [...]
>
> > +static int fsl_asrc_dma_pcm_new(struct snd_soc_pcm_runtime *rtd)
> > +{
> > + struct snd_card *card = rtd->card->snd_card;
> > + struct snd_pcm_substream *substream;
> > + struct snd_pcm *pcm = rtd->pcm;
> > + u64 dma_mask = DMA_BIT_MASK(32);
> > + int ret, i;
> > +
> > + if (!card->dev->dma_mask)
> > + card->dev->dma_mask = &dma_mask;
>
> dma_mask will go out of scope after fsl_asrc_dma_pcm_new returns,
> resulting in a stray pointer in card->dev->dma_mask.
Will fix them in v3.
Thank you
Nicolin
^ permalink raw reply
* RE: [PATCH v2 1/7] dt: Introduce the FMan 10 Gb/s MDIO binding
From: Shaohui Xie @ 2014-07-25 3:11 UTC (permalink / raw)
To: Shruti Kanetkar, Scott Wood; +Cc: linuxppc-dev@lists.ozlabs.org
In-Reply-To: <1405541831-1277-1-git-send-email-Shruti@Freescale.com>
SGVsbG8sIFNjb3R0LCBTaHJ1dGksDQoNClRoZXJlIGFyZSB0d28gdHlwZXMgTURJTyBjb250cm9s
bGVycywgZWFjaCBNQUMgaGFzIGEgY29ycmVzcG9uZGluZyBNRElPIGNvbnRyb2xsZXIgdG8gYWNj
ZXNzIGludGVybmFsIFBIWXMgaW5zaWRlIFNlcmRlcywgYW5vdGhlciBpcyBkZWRpY2F0ZWQgTURJ
TyBjb250cm9sbGVyIHRvIGFjY2VzcyBleHRlcm5hbCBQSFlzIChQSFlzIG9uIGJvYXJkIG9yIHJp
c2VyIGNhcmQsIGV0YykuIEN1cnJlbnQgTURJTyBkcml2ZXIgZG9lcyBub3QgZGlzdGluZ3Vpc2gg
dGhlIHR3byBjb250cm9sbGVycywgaWYgd2UgbmVlZCB0byBkaXN0aW5ndWlzaCB0aGUgY29udHJv
bGxlcnMsIHdoYXQgY291bGQgYmUgdGhlIHByb3BlciB3YXk/IFBlcmhhcHMgYSBuZXcgcHJvcGVy
dHkgaXMgbmVlZGVkIGluIGR0cz8NClBsZWFzZSBjb21tZW50IQ0KVGhhbmtzIQ0KDQpCZXN0IFJl
Z2FyZHMsIA0KU2hhb2h1aSBYaWUNCj4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCj4gRnJv
bTogTGludXhwcGMtZGV2IFttYWlsdG86bGludXhwcGMtZGV2LQ0KPiBib3VuY2VzK2IyMTk4OT1m
cmVlc2NhbGUuY29tQGxpc3RzLm96bGFicy5vcmddIE9uIEJlaGFsZiBPZiBTaHJ1dGkNCj4gS2Fu
ZXRrYXINCj4gU2VudDogVGh1cnNkYXksIEp1bHkgMTcsIDIwMTQgNDoxNyBBTQ0KPiBUbzogV29v
ZCBTY290dC1CMDc0MjENCj4gQ2M6IEthbmV0a2FyIFNocnV0aS1CNDQ0NTQ7IGxpbnV4cHBjLWRl
dkBsaXN0cy5vemxhYnMub3JnDQo+IFN1YmplY3Q6IFtQQVRDSCB2MiAxLzddIGR0OiBJbnRyb2R1
Y2UgdGhlIEZNYW4gMTAgR2IvcyBNRElPIGJpbmRpbmcNCj4gDQo+IFRoaXMgZG9jdW1lbnQgY292
ZXJzIEZNYW52MiBhbmQgRk1hbnYzIG1FTUFDIE1ESU8NCj4gDQo+IFNpZ25lZC1vZmYtYnk6IFNo
cnV0aSBLYW5ldGthciA8U2hydXRpQEZyZWVzY2FsZS5jb20+DQo+IC0tLQ0KPiAgLi4uL2Rldmlj
ZXRyZWUvYmluZGluZ3MvbmV0L2ZzbC14Z21hYy1waHkudHh0ICAgICAgfCAyOQ0KPiArKysrKysr
KysrKysrKysrKysrKysrDQo+ICAxIGZpbGUgY2hhbmdlZCwgMjkgaW5zZXJ0aW9ucygrKQ0KPiAg
Y3JlYXRlIG1vZGUgMTAwNjQ0IERvY3VtZW50YXRpb24vZGV2aWNldHJlZS9iaW5kaW5ncy9uZXQv
ZnNsLXhnbWFjLQ0KPiBwaHkudHh0DQo+IA0KPiBkaWZmIC0tZ2l0IGEvRG9jdW1lbnRhdGlvbi9k
ZXZpY2V0cmVlL2JpbmRpbmdzL25ldC9mc2wteGdtYWMtcGh5LnR4dA0KPiBiL0RvY3VtZW50YXRp
b24vZGV2aWNldHJlZS9iaW5kaW5ncy9uZXQvZnNsLXhnbWFjLXBoeS50eHQNCj4gbmV3IGZpbGUg
bW9kZSAxMDA2NDQNCj4gaW5kZXggMDAwMDAwMC4uMmYyZDU2ZA0KPiAtLS0gL2Rldi9udWxsDQo+
ICsrKyBiL0RvY3VtZW50YXRpb24vZGV2aWNldHJlZS9iaW5kaW5ncy9uZXQvZnNsLXhnbWFjLXBo
eS50eHQNCj4gQEAgLTAsMCArMSwyOSBAQA0KPiArKk1ESU8gSU8gZGV2aWNlDQo+ICsNCj4gK1Ro
ZSBNRElPIGlzIGEgYnVzIHRvIHdoaWNoIHRoZSBQSFkgZGV2aWNlcyBhcmUgY29ubmVjdGVkLiBG
b3IgZWFjaA0KPiArZGV2aWNlIHRoYXQgZXhpc3RzIG9uIHRoaXMgYnVzLCBhIGNoaWxkIG5vZGUg
c2hvdWxkIGJlIGNyZWF0ZWQuDQo+ICsNCj4gK1JlcXVpcmVkIHByb3BlcnRpZXM6DQo+ICsJLSBj
b21wYXRpYmxlOiBDdXJyZW50bHkgc3VwcG9ydGVkIHN0cmluZ3MvZGV2aWNlcyBhcmU6DQo+ICsJ
CQktICJmc2wsZm1hbi14bWRpbyIgKE11c3QgYmUgdXNlZCBmb3IgMTAgR2IvcyBNRElPIGZyb20N
Cj4gRk1hbiB2Mi4NCj4gKwkJCSAgQ2FuIGJlIHVzZWQgZm9yIDEvMTAgR2IvcyBNRElPIGZyb20g
Rk1hbiB2MykNCj4gKwkJCS0gImZzbCxmbWFuLW1lbWFjLW1kaW8iIChNdXN0IGJlIHVzZWQgZm9y
IDEvMTAgR2Ivcw0KPiBNRElPIGZyb20NCj4gKwkJCSAgRk1hbiB2MykNCj4gKwktIHJlZw0KPiAr
DQo+ICsJRXhhbXBsZSBmb3IgRk1hbiB2MjoNCj4gKwkJbWRpb0BmMTAwMCB7DQo+ICsJCQkjYWRk
cmVzcy1jZWxscyA9IDwxPjsNCj4gKwkJCSNzaXplLWNlbGxzID0gPDA+Ow0KPiArCQkJY29tcGF0
aWJsZSA9ICJmc2wsZm1hbi14bWRpbyI7DQo+ICsJCQlyZWcgPSA8MHhmMTAwMCAweDEwMDA+Ow0K
PiArCQl9Ow0KPiArDQo+ICsJRXhhbXBsZSBmb3IgRk1hbiB2MzoNCj4gKw0KPiArCQltZGlvQGYx
MDAwIHsNCj4gKwkJCSNhZGRyZXNzLWNlbGxzID0gPDE+Ow0KPiArCQkJI3NpemUtY2VsbHMgPSA8
MD47DQo+ICsJCQljb21wYXRpYmxlID0gImZzbCxmbWFuLW1lbWFjLW1kaW8iLCAiZnNsLGZtYW4t
eG1kaW8iOw0KPiArCQkJcmVnID0gPDB4ZjEwMDAgMHgxMDAwPjsNCj4gKwkJfQ0KPiAtLQ0KPiAx
LjguMy4xDQo+IA0KPiBfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19f
X19fXw0KPiBMaW51eHBwYy1kZXYgbWFpbGluZyBsaXN0DQo+IExpbnV4cHBjLWRldkBsaXN0cy5v
emxhYnMub3JnDQo+IGh0dHBzOi8vbGlzdHMub3psYWJzLm9yZy9saXN0aW5mby9saW51eHBwYy1k
ZXYNCg==
^ permalink raw reply
* [PATCH v3 0/2] Add Freescale ASRC driver
From: Nicolin Chen @ 2014-07-25 4:03 UTC (permalink / raw)
To: broonie
Cc: mark.rutland, devicetree, alsa-devel, b42378, timur, pawel.moll,
ijc+devicetree, tiwai, linux-kernel, rdunlap, b02247, lgirdwood,
perex, robh+dt, galak, grant.likely, tklauser, shawn.guo,
linuxppc-dev
This series of patches add Freescale ASRC module driver support along
with the extra request in imx-sdma structure required by SDMA Device
to Device script.
The previous version has been in the maillist for nearly six months
without any comment and reply. So I decide to drop the SDMA part's
change since this series focus on ASRC support (Later, we can send
SDMA part separately.) so that ASRC driver may get upstream first.
Changelog
v3:
* PATCH-1: Added an Acked-by from Shawn Guo
* PATCH-2: Dropped THIS_MODULE as Tobias suggests
* PATCH-2: Use dma_coerce_mask_and_coherent() in fsl_asrc_dma_pcm_new()
* PATCH-2: Added substream check in fsl_asrc_dma_pcm_new()
v2:
* PATCH-1: Dropped the change to SDMA driver.
* PATCH-2: Dropped useless member in private data structures.
* PATCH-2: Refined some comments in fsl_asrc_dma.c driver.
* PATCH-2: Refined commit comments.
Nicolin Chen (2):
ARM: imx: Add the secondary request into the structure for imx-sdma
ASoC: fsl_asrc: Add ASRC ASoC CPU DAI and platform drivers
.../devicetree/bindings/sound/fsl,asrc.txt | 62 ++
include/linux/platform_data/dma-imx.h | 1 +
sound/soc/fsl/Kconfig | 9 +
sound/soc/fsl/Makefile | 2 +
sound/soc/fsl/fsl_asrc.c | 1030 ++++++++++++++++++++
sound/soc/fsl/fsl_asrc.h | 461 +++++++++
sound/soc/fsl/fsl_asrc_dma.c | 386 ++++++++
7 files changed, 1951 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/fsl,asrc.txt
create mode 100644 sound/soc/fsl/fsl_asrc.c
create mode 100644 sound/soc/fsl/fsl_asrc.h
create mode 100644 sound/soc/fsl/fsl_asrc_dma.c
--
1.8.4
^ permalink raw reply
* [PATCH v3 1/2] ARM: imx: Add the secondary request into the structure for imx-sdma
From: Nicolin Chen @ 2014-07-25 4:03 UTC (permalink / raw)
To: broonie
Cc: mark.rutland, devicetree, alsa-devel, b42378, timur, pawel.moll,
ijc+devicetree, tiwai, linux-kernel, rdunlap, b02247, lgirdwood,
perex, robh+dt, galak, grant.likely, tklauser, shawn.guo,
linuxppc-dev
In-Reply-To: <cover.1406259578.git.nicoleotsuka@gmail.com>
SDMA supports device to device (per_2_per) scripts to handle DMA transfering
between two peripheral devices. The per_2_per script, however, needs two dma
requests from two sides while the current structure only defined one request.
So this patch just simply adds the secondary request so as to let SDMA and
its user to add its implementation later.
[ Both change in the SDMA driver and its users like Freescale ASRC ASoC driver
should be taken along with this change in order to truly support per_2_per
sciprts. However, we here make an expediency by adding this first so that
we can add either side later since this patch won't break any function and
meanwhile it can make merge window more smoothly: we don't need to apply the
change inside dmaengine branch via ASoC tree any more. -- Nicolin ]
Signed-off-by: Nicolin Chen <nicoleotsuka@gmail.com>
Acked-by: Shawn Guo <shawn.guo@linaro.org>
---
include/linux/platform_data/dma-imx.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/linux/platform_data/dma-imx.h b/include/linux/platform_data/dma-imx.h
index 7aa0e89..6a1357d 100644
--- a/include/linux/platform_data/dma-imx.h
+++ b/include/linux/platform_data/dma-imx.h
@@ -51,6 +51,7 @@ enum imx_dma_prio {
struct imx_dma_data {
int dma_request; /* DMA request line */
+ int dma_request2; /* secondary DMA request line */
enum sdma_peripheral_type peripheral_type;
int priority;
};
--
1.8.4
^ permalink raw reply related
* [PATCH v3 2/2] ASoC: fsl_asrc: Add ASRC ASoC CPU DAI and platform drivers
From: Nicolin Chen @ 2014-07-25 4:03 UTC (permalink / raw)
To: broonie
Cc: mark.rutland, devicetree, alsa-devel, b42378, timur, pawel.moll,
ijc+devicetree, tiwai, linux-kernel, rdunlap, b02247, lgirdwood,
perex, robh+dt, galak, grant.likely, tklauser, shawn.guo,
linuxppc-dev
In-Reply-To: <cover.1406259578.git.nicoleotsuka@gmail.com>
The Asynchronous Sample Rate Converter (ASRC) converts the sampling rate of a
signal associated with an input clock into a signal associated with a different
output clock. The driver currently works as a Front End of DPCM with other Back
Ends DAI links such as ESAI<->CS42888 and SSI<->WM8962 and SAI. It converts the
original sample rate to a common rate supported by Back Ends for playback while
converts the common rate of Back Ends to a desired rate for capture. It has 3
pairs to support three different substreams within totally 10 channels.
Signed-off-by: Nicolin Chen <nicoleotsuka@gmail.com>
---
.../devicetree/bindings/sound/fsl,asrc.txt | 62 ++
sound/soc/fsl/Kconfig | 9 +
sound/soc/fsl/Makefile | 2 +
sound/soc/fsl/fsl_asrc.c | 1030 ++++++++++++++++++++
sound/soc/fsl/fsl_asrc.h | 461 +++++++++
sound/soc/fsl/fsl_asrc_dma.c | 386 ++++++++
6 files changed, 1950 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/fsl,asrc.txt
create mode 100644 sound/soc/fsl/fsl_asrc.c
create mode 100644 sound/soc/fsl/fsl_asrc.h
create mode 100644 sound/soc/fsl/fsl_asrc_dma.c
diff --git a/Documentation/devicetree/bindings/sound/fsl,asrc.txt b/Documentation/devicetree/bindings/sound/fsl,asrc.txt
new file mode 100644
index 0000000..637028c
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/fsl,asrc.txt
@@ -0,0 +1,62 @@
+Freescale Asynchronous Sample Rate Converter (ASRC) Controller
+
+The Asynchronous Sample Rate Converter (ASRC) converts the sampling rate of a
+signal associated with an input clock into a signal associated with a different
+output clock. The driver currently works as a Front End of DPCM with other Back
+Ends Audio controller such as ESAI, SSI and SAI. It has three pairs to support
+three substreams within totally 10 channels.
+
+Required properties:
+
+ - compatible : Compatible list, must contain "fsl,imx35-asrc" or
+ "fsl,imx53-asrc".
+
+ - reg : Offset and length of the register set for the device.
+
+ - interrupts : Contains the spdif interrupt.
+
+ - dmas : Generic dma devicetree binding as described in
+ Documentation/devicetree/bindings/dma/dma.txt.
+
+ - dma-names : Six dmas have to be defined: "rxa", "rxb", "rxc", "txa", "txb",
+ and "txc".
+
+ - clocks : Contains an entry for each entry in clock-names.
+
+ - clock-names : Includes the following entries:
+ "mem" Peripheral access clock to access registers.
+ "ipg" Peripheral clock to driver module.
+ "asrck_<0-f>" Clock sources for input and output clock.
+
+ - big-endian : If this property is absent, the native endian mode will
+ be in use as default, or the big endian mode will be in use
+ for all the device registers.
+
+ - fsl,asrc-rate : Defines a mutual sample rate used by Back End DAI link.
+
+ - fsl,asrc-width : Defines a mutual sample width used by Back End DAI link.
+
+Example:
+
+asrc: asrc@02034000 {
+ compatible = "fsl,imx53-asrc";
+ reg = <0x02034000 0x4000>;
+ interrupts = <0 50 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clks 107>, <&clks 107>, <&clks 0>,
+ <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>,
+ <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>,
+ <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>,
+ <&clks 107>, <&clks 0>, <&clks 0>;
+ clock-names = "mem", "ipg", "asrck0",
+ "asrck_1", "asrck_2", "asrck_3", "asrck_4",
+ "asrck_5", "asrck_6", "asrck_7", "asrck_8",
+ "asrck_9", "asrck_a", "asrck_b", "asrck_c",
+ "asrck_d", "asrck_e", "asrck_f";
+ dmas = <&sdma 17 23 1>, <&sdma 18 23 1>, <&sdma 19 23 1>,
+ <&sdma 20 23 1>, <&sdma 21 23 1>, <&sdma 22 23 1>;
+ dma-names = "rxa", "rxb", "rxc",
+ "txa", "txb", "txc";
+ fsl,asrc-rate = <48000>;
+ fsl,asrc-width = <16>;
+ status = "okay";
+};
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 2d60e37..e06fbfe 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -2,6 +2,15 @@ menu "SoC Audio for Freescale CPUs"
comment "Common SoC Audio options for Freescale CPUs:"
+config SND_SOC_FSL_ASRC
+ tristate "Asynchronous Sample Rate Converter (ASRC) module support"
+ select REGMAP_MMIO
+ help
+ Say Y if you want to add Asynchronous Sample Rate Converter (ASRC)
+ support for the Freescale CPUs.
+ This option is only useful for out-of-tree drivers since
+ in-tree drivers select it automatically.
+
config SND_SOC_FSL_SAI
tristate "Synchronous Audio Interface (SAI) module support"
select REGMAP_MMIO
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index ce49570..ff7ee2c 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -11,6 +11,7 @@ snd-soc-p1022-rdk-objs := p1022_rdk.o
obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
# Freescale SSI/DMA/SAI/SPDIF Support
+snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o
snd-soc-fsl-sai-objs := fsl_sai.o
snd-soc-fsl-ssi-y := fsl_ssi.o
snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o
@@ -18,6 +19,7 @@ snd-soc-fsl-spdif-objs := fsl_spdif.o
snd-soc-fsl-esai-objs := fsl_esai.o
snd-soc-fsl-utils-objs := fsl_utils.o
snd-soc-fsl-dma-objs := fsl_dma.o
+obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o
obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o
obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
new file mode 100644
index 0000000..800e88e
--- /dev/null
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -0,0 +1,1030 @@
+/*
+ * Freescale ASRC ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <nicoleotsuka@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_data/dma-imx.h>
+#include <linux/pm_runtime.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#include "fsl_asrc.h"
+
+#define IDEAL_RATIO_DECIMAL_DEPTH 26
+
+#define pair_err(fmt, ...) \
+ dev_err(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
+
+#define pair_dbg(fmt, ...) \
+ dev_dbg(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
+
+/* Sample rates are aligned with that defined in pcm.h file */
+static const u8 process_option[][8][2] = {
+ /* 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176kHz 192kHz */
+ {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 5512Hz */
+ {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 8kHz */
+ {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 11025Hz */
+ {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 16kHz */
+ {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 22050Hz */
+ {{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},}, /* 32kHz */
+ {{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 44.1kHz */
+ {{0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 48kHz */
+ {{1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},}, /* 64kHz */
+ {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 88.2kHz */
+ {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 96kHz */
+ {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 176kHz */
+ {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 192kHz */
+};
+
+/* Corresponding to process_option */
+static int supported_input_rate[] = {
+ 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200,
+ 96000, 176400, 192000,
+};
+
+static int supported_asrc_rate[] = {
+ 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000,
+};
+
+/**
+ * The following tables map the relationship between asrc_inclk/asrc_outclk in
+ * fsl_asrc.h and the registers of ASRCSR
+ */
+static unsigned char input_clk_map_imx35[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
+};
+
+static unsigned char output_clk_map_imx35[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
+};
+
+/* i.MX53 uses the same map for input and output */
+static unsigned char input_clk_map_imx53[] = {
+/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */
+ 0x0, 0x1, 0x2, 0x7, 0x4, 0x5, 0x6, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xe, 0xd,
+};
+
+static unsigned char output_clk_map_imx53[] = {
+/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */
+ 0x8, 0x9, 0xa, 0x7, 0xc, 0x5, 0x6, 0xb, 0x0, 0x1, 0x2, 0x3, 0x4, 0xf, 0xe, 0xd,
+};
+
+static unsigned char *clk_map[2];
+
+enum fsl_asrc_type {
+ IMX35_ASRC,
+ IMX53_ASRC,
+};
+
+static const struct platform_device_id fsl_asrc_devtype[] = {
+ {
+ .name = "imx35-asrc",
+ .driver_data = IMX35_ASRC,
+ }, {
+ .name = "imx53-asrc",
+ .driver_data = IMX53_ASRC,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(platform, fsl_asrc_devtype);
+
+static const struct of_device_id fsl_asrc_ids[] = {
+ {
+ .compatible = "fsl,imx35-asrc",
+ .data = &fsl_asrc_devtype[IMX35_ASRC],
+ }, {
+ .compatible = "fsl,imx53-asrc",
+ .data = &fsl_asrc_devtype[IMX53_ASRC],
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, fsl_asrc_ids);
+
+static irqreturn_t fsl_asrc_isr(int irq, void *dev_id)
+{
+ struct fsl_asrc *asrc_priv = (struct fsl_asrc *)dev_id;
+ struct device *dev = &asrc_priv->pdev->dev;
+ enum asrc_pair_index index;
+ u32 status;
+
+ regmap_read(asrc_priv->regmap, REG_ASRSTR, &status);
+
+ /* Clean overload error */
+ regmap_write(asrc_priv->regmap, REG_ASRSTR, ASRSTR_AOLE);
+
+ /*
+ * We here use dev_dbg() for all exceptions because ASRC itself does
+ * not care if FIFO overflowed or underrun while a warning in the
+ * interrupt would result a ridged conversion.
+ */
+ for (index = ASRC_PAIR_A; index < ASRC_PAIR_MAX_NUM; index++) {
+ if (!asrc_priv->pair[index])
+ continue;
+
+ if (status & ASRSTR_ATQOL) {
+ asrc_priv->pair[index]->error |= ASRC_TASK_Q_OVERLOAD;
+ dev_dbg(dev, "ASRC Task Queue FIFO overload");
+ }
+
+ if (status & ASRSTR_AOOL(index)) {
+ asrc_priv->pair[index]->error |= ASRC_OUTPUT_TASK_OVERLOAD;
+ pair_dbg("Output Task Overload");
+ }
+
+ if (status & ASRSTR_AIOL(index)) {
+ asrc_priv->pair[index]->error |= ASRC_INPUT_TASK_OVERLOAD;
+ pair_dbg("Input Task Overload");
+ }
+
+ if (status & ASRSTR_AODO(index)) {
+ asrc_priv->pair[index]->error |= ASRC_OUTPUT_BUFFER_OVERFLOW;
+ pair_dbg("Output Data Buffer has overflowed");
+ }
+
+ if (status & ASRSTR_AIDU(index)) {
+ asrc_priv->pair[index]->error |= ASRC_INPUT_BUFFER_UNDERRUN;
+ pair_dbg("Input Data Buffer has underflowed");
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * Request ASRC pair
+ *
+ * It assigns pair by the order of A->C->B because allocation of pair B,
+ * within range [ANCA, ANCA+ANCB-1], depends on the channels of pair A
+ * while pair A and pair C are comparatively independent.
+ */
+static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
+{
+ enum asrc_pair_index index = ASRC_INVALID_PAIR;
+ struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ struct device *dev = &asrc_priv->pdev->dev;
+ unsigned long lock_flags;
+ int i, ret = 0;
+
+ spin_lock_irqsave(&asrc_priv->lock, lock_flags);
+
+ for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) {
+ if (asrc_priv->pair[i] != NULL)
+ continue;
+
+ index = i;
+
+ if (i != ASRC_PAIR_B)
+ break;
+ }
+
+ if (index == ASRC_INVALID_PAIR) {
+ dev_err(dev, "all pairs are busy now\n");
+ ret = -EBUSY;
+ } else if (asrc_priv->channel_avail < channels) {
+ dev_err(dev, "can't afford required channels: %d\n", channels);
+ ret = -EINVAL;
+ } else {
+ asrc_priv->channel_avail -= channels;
+ asrc_priv->pair[index] = pair;
+ pair->channels = channels;
+ pair->index = index;
+ }
+
+ spin_unlock_irqrestore(&asrc_priv->lock, lock_flags);
+
+ return ret;
+}
+
+/**
+ * Release ASRC pair
+ *
+ * It clears the resource from asrc_priv and releases the occupied channels.
+ */
+static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
+{
+ struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ enum asrc_pair_index index = pair->index;
+ unsigned long lock_flags;
+
+ /* Make sure the pair is disabled */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ ASRCTR_ASRCEi_MASK(index), 0);
+
+ spin_lock_irqsave(&asrc_priv->lock, lock_flags);
+
+ asrc_priv->channel_avail += pair->channels;
+ asrc_priv->pair[index] = NULL;
+ pair->error = 0;
+
+ spin_unlock_irqrestore(&asrc_priv->lock, lock_flags);
+}
+
+/**
+ * Configure input and output thresholds
+ */
+static int fsl_asrc_set_watermarks(struct fsl_asrc_pair *pair, u32 in, u32 out)
+{
+ struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ enum asrc_pair_index index = pair->index;
+
+ regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index),
+ ASRMCRi_EXTTHRSHi_MASK |
+ ASRMCRi_INFIFO_THRESHOLD_MASK |
+ ASRMCRi_OUTFIFO_THRESHOLD_MASK,
+ ASRMCRi_EXTTHRSHi |
+ ASRMCRi_INFIFO_THRESHOLD(in) |
+ ASRMCRi_OUTFIFO_THRESHOLD(out));
+
+ return 0;
+}
+
+/**
+ * Calculate the total divisor between asrck clock rate and sample rate
+ *
+ * It follows the formula clk_rate = samplerate * (2 ^ prescaler) * divider
+ */
+static u32 fsl_asrc_cal_asrck_divisor(struct fsl_asrc_pair *pair, u32 div)
+{
+ u32 ps;
+
+ /* Calculate the divisors: prescaler [2^0, 2^7], divder [1, 8] */
+ for (ps = 0; div > 8; ps++)
+ div >>= 1;
+
+ return ((div - 1) << ASRCDRi_AxCPi_WIDTH) | ps;
+}
+
+/**
+ * Calculate and set the ratio for Ideal Ratio mode only
+ *
+ * The ratio is a 32-bit fixed point value with 26 fractional bits.
+ */
+static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair,
+ int inrate, int outrate)
+{
+ struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ enum asrc_pair_index index = pair->index;
+ unsigned long ratio;
+ int i;
+
+ if (!outrate) {
+ pair_err("output rate should not be zero\n");
+ return -EINVAL;
+ }
+
+ /* Calculate the intergal part of the ratio */
+ ratio = (inrate / outrate) << IDEAL_RATIO_DECIMAL_DEPTH;
+
+ /* ... and then the 26 depth decimal part */
+ inrate %= outrate;
+
+ for (i = 1; i <= IDEAL_RATIO_DECIMAL_DEPTH; i++) {
+ inrate <<= 1;
+
+ if (inrate < outrate)
+ continue;
+
+ ratio |= 1 << (IDEAL_RATIO_DECIMAL_DEPTH - i);
+ inrate -= outrate;
+
+ if (!inrate)
+ break;
+ }
+
+ regmap_write(asrc_priv->regmap, REG_ASRIDRL(index), ratio);
+ regmap_write(asrc_priv->regmap, REG_ASRIDRH(index), ratio >> 24);
+
+ return 0;
+}
+
+/**
+ * Configure the assigned ASRC pair
+ *
+ * It configures those ASRC registers according to a configuration instance
+ * of struct asrc_config which includes in/output sample rate, width, channel
+ * and clock settings.
+ */
+static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
+{
+ struct asrc_config *config = pair->config;
+ struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ enum asrc_pair_index index = pair->index;
+ u32 inrate = config->input_sample_rate, indiv;
+ u32 outrate = config->output_sample_rate, outdiv;
+ bool ideal = config->inclk == INCLK_NONE;
+ u32 clk_index[2], div[2];
+ int in, out, channels;
+ struct clk *clk;
+
+ if (!config) {
+ pair_err("invalid pair config\n");
+ return -EINVAL;
+ }
+
+ /* Validate channels */
+ if (config->channel_num < 1 || config->channel_num > 10) {
+ pair_err("does not support %d channels\n", config->channel_num);
+ return -EINVAL;
+ }
+
+ /* Validate output width */
+ if (config->output_word_width == ASRC_WIDTH_8_BIT) {
+ pair_err("does not support 8bit width output\n");
+ return -EINVAL;
+ }
+
+ /* Validate input and output sample rates */
+ for (in = 0; in < ARRAY_SIZE(supported_input_rate); in++)
+ if (inrate == supported_input_rate[in])
+ break;
+
+ if (in == ARRAY_SIZE(supported_input_rate)) {
+ pair_err("unsupported input sample rate: %dHz\n", inrate);
+ return -EINVAL;
+ }
+
+ for (out = 0; out < ARRAY_SIZE(supported_asrc_rate); out++)
+ if (outrate == supported_asrc_rate[out])
+ break;
+
+ if (out == ARRAY_SIZE(supported_asrc_rate)) {
+ pair_err("unsupported output sample rate: %dHz\n", outrate);
+ return -EINVAL;
+ }
+
+ /* Validate input and output clock sources */
+ clk_index[IN] = clk_map[IN][config->inclk];
+ clk_index[OUT] = clk_map[OUT][config->outclk];
+
+ /* We only have output clock for ideal ratio mode */
+ clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]];
+
+ div[IN] = clk_get_rate(clk) / inrate;
+ if (div[IN] == 0) {
+ pair_err("failed to support input sample rate %dHz by asrck_%x",
+ inrate, clk_index[ideal ? OUT : IN]);
+ return -EINVAL;
+ }
+
+ clk = asrc_priv->asrck_clk[clk_index[OUT]];
+
+ /* Use fixed output rate for Ideal Ratio mode (INCLK_NONE) */
+ if (ideal)
+ div[OUT] = clk_get_rate(clk) / IDEAL_RATIO_RATE;
+ else
+ div[OUT] = clk_get_rate(clk) / outrate;
+
+ if (div[OUT] == 0) {
+ pair_err("failed to support output sample rate %dHz by asrck_%x",
+ outrate, clk_index[OUT]);
+ return -EINVAL;
+ }
+
+ /* Set the channel number */
+ channels = config->channel_num;
+
+ if (asrc_priv->channel_bits < 4)
+ channels /= 2;
+
+ /* Update channels for current pair */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCNCR,
+ ASRCNCR_ANCi_MASK(index, asrc_priv->channel_bits),
+ ASRCNCR_ANCi(index, channels, asrc_priv->channel_bits));
+
+ /* Default setting: Automatic selection for processing mode */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ ASRCTR_ATSi_MASK(index), ASRCTR_ATS(index));
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ ASRCTR_USRi_MASK(index), 0);
+
+ /* Set the input and output clock sources */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCSR,
+ ASRCSR_AICSi_MASK(index) | ASRCSR_AOCSi_MASK(index),
+ ASRCSR_AICS(index, clk_index[IN]) |
+ ASRCSR_AOCS(index, clk_index[OUT]));
+
+ /* Calculate the input clock divisors */
+ indiv = fsl_asrc_cal_asrck_divisor(pair, div[IN]);
+ outdiv = fsl_asrc_cal_asrck_divisor(pair, div[OUT]);
+
+ /* Suppose indiv and outdiv includes prescaler, so add its MASK too */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCDR(index),
+ ASRCDRi_AOCPi_MASK(index) | ASRCDRi_AICPi_MASK(index) |
+ ASRCDRi_AOCDi_MASK(index) | ASRCDRi_AICDi_MASK(index),
+ ASRCDRi_AOCP(index, outdiv) | ASRCDRi_AICP(index, indiv));
+
+ /* Implement word_width configurations */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRMCR1(index),
+ ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK,
+ ASRMCR1i_OW16(config->output_word_width) |
+ ASRMCR1i_IWD(config->input_word_width));
+
+ /* Enable BUFFER STALL */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index),
+ ASRMCRi_BUFSTALLi_MASK, ASRMCRi_BUFSTALLi);
+
+ /* Set default thresholds for input and output FIFO */
+ fsl_asrc_set_watermarks(pair, ASRC_INPUTFIFO_THRESHOLD,
+ ASRC_INPUTFIFO_THRESHOLD);
+
+ /* Configure the followings only for Ideal Ratio mode */
+ if (!ideal)
+ return 0;
+
+ /* Clear ASTSx bit to use Ideal Ratio mode */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ ASRCTR_ATSi_MASK(index), 0);
+
+ /* Enable Ideal Ratio mode */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ ASRCTR_IDRi_MASK(index) | ASRCTR_USRi_MASK(index),
+ ASRCTR_IDR(index) | ASRCTR_USR(index));
+
+ /* Apply configurations for pre- and post-processing */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCFG,
+ ASRCFG_PREMODi_MASK(index) | ASRCFG_POSTMODi_MASK(index),
+ ASRCFG_PREMOD(index, process_option[in][out][0]) |
+ ASRCFG_POSTMOD(index, process_option[in][out][1]));
+
+ return fsl_asrc_set_ideal_ratio(pair, inrate, outrate);
+}
+
+/**
+ * Start the assigned ASRC pair
+ *
+ * It enables the assigned pair and makes it stopped at the stall level.
+ */
+static void fsl_asrc_start_pair(struct fsl_asrc_pair *pair)
+{
+ struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ enum asrc_pair_index index = pair->index;
+ int reg, retry = 10, i;
+
+ /* Enable the current pair */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ ASRCTR_ASRCEi_MASK(index), ASRCTR_ASRCE(index));
+
+ /* Wait for status of initialization */
+ do {
+ udelay(5);
+ regmap_read(asrc_priv->regmap, REG_ASRCFG, ®);
+ reg &= ASRCFG_INIRQi_MASK(index);
+ } while (!reg && --retry);
+
+ /* Make the input fifo to ASRC STALL level */
+ regmap_read(asrc_priv->regmap, REG_ASRCNCR, ®);
+ for (i = 0; i < pair->channels * 4; i++)
+ regmap_write(asrc_priv->regmap, REG_ASRDI(index), 0);
+
+ /* Enable overload interrupt */
+ regmap_write(asrc_priv->regmap, REG_ASRIER, ASRIER_AOLIE);
+}
+
+/**
+ * Stop the assigned ASRC pair
+ */
+static void fsl_asrc_stop_pair(struct fsl_asrc_pair *pair)
+{
+ struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ enum asrc_pair_index index = pair->index;
+
+ /* Stop the current pair */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ ASRCTR_ASRCEi_MASK(index), 0);
+}
+
+/**
+ * Get DMA channel according to the pair and direction.
+ */
+struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir)
+{
+ struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ enum asrc_pair_index index = pair->index;
+ char name[4];
+
+ sprintf(name, "%cx%c", dir == IN ? 'r' : 't', index + 'a');
+
+ return dma_request_slave_channel(&asrc_priv->pdev->dev, name);
+}
+EXPORT_SYMBOL_GPL(fsl_asrc_get_dma_channel);
+
+static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
+ int width = snd_pcm_format_width(params_format(params));
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+ unsigned int channels = params_channels(params);
+ unsigned int rate = params_rate(params);
+ struct asrc_config config;
+ int word_width, ret;
+
+ ret = fsl_asrc_request_pair(channels, pair);
+ if (ret) {
+ dev_err(dai->dev, "fail to request asrc pair\n");
+ return ret;
+ }
+
+ pair->config = &config;
+
+ if (width == 16)
+ width = ASRC_WIDTH_16_BIT;
+ else
+ width = ASRC_WIDTH_24_BIT;
+
+ if (asrc_priv->asrc_width == 16)
+ word_width = ASRC_WIDTH_16_BIT;
+ else
+ word_width = ASRC_WIDTH_24_BIT;
+
+ config.pair = pair->index;
+ config.channel_num = channels;
+ config.inclk = INCLK_NONE;
+ config.outclk = OUTCLK_ASRCK1_CLK;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ config.input_word_width = width;
+ config.output_word_width = word_width;
+ config.input_sample_rate = rate;
+ config.output_sample_rate = asrc_priv->asrc_rate;
+ } else {
+ config.input_word_width = word_width;
+ config.output_word_width = width;
+ config.input_sample_rate = asrc_priv->asrc_rate;
+ config.output_sample_rate = rate;
+ }
+
+ ret = fsl_asrc_config_pair(pair);
+ if (ret) {
+ dev_err(dai->dev, "fail to config asrc pair\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fsl_asrc_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+
+ if (pair)
+ fsl_asrc_release_pair(pair);
+
+ return 0;
+}
+
+static int fsl_asrc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ fsl_asrc_start_pair(pair);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ fsl_asrc_stop_pair(pair);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops fsl_asrc_dai_ops = {
+ .hw_params = fsl_asrc_dai_hw_params,
+ .hw_free = fsl_asrc_dai_hw_free,
+ .trigger = fsl_asrc_dai_trigger,
+};
+
+static int fsl_asrc_dai_probe(struct snd_soc_dai *dai)
+{
+ struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, &asrc_priv->dma_params_tx,
+ &asrc_priv->dma_params_rx);
+
+ return 0;
+}
+
+#define FSL_ASRC_RATES SNDRV_PCM_RATE_8000_192000
+#define FSL_ASRC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FORMAT_S20_3LE)
+
+static struct snd_soc_dai_driver fsl_asrc_dai = {
+ .probe = fsl_asrc_dai_probe,
+ .playback = {
+ .stream_name = "ASRC-Playback",
+ .channels_min = 1,
+ .channels_max = 10,
+ .rates = FSL_ASRC_RATES,
+ .formats = FSL_ASRC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASRC-Capture",
+ .channels_min = 1,
+ .channels_max = 10,
+ .rates = FSL_ASRC_RATES,
+ .formats = FSL_ASRC_FORMATS,
+ },
+ .ops = &fsl_asrc_dai_ops,
+};
+
+static const struct snd_soc_component_driver fsl_asrc_component = {
+ .name = "fsl-asrc-dai",
+};
+
+static bool fsl_asrc_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_ASRCTR:
+ case REG_ASRIER:
+ case REG_ASRCNCR:
+ case REG_ASRCFG:
+ case REG_ASRCSR:
+ case REG_ASRCDR1:
+ case REG_ASRCDR2:
+ case REG_ASRSTR:
+ case REG_ASRPM1:
+ case REG_ASRPM2:
+ case REG_ASRPM3:
+ case REG_ASRPM4:
+ case REG_ASRPM5:
+ case REG_ASRTFR1:
+ case REG_ASRCCR:
+ case REG_ASRDOA:
+ case REG_ASRDOB:
+ case REG_ASRDOC:
+ case REG_ASRIDRHA:
+ case REG_ASRIDRLA:
+ case REG_ASRIDRHB:
+ case REG_ASRIDRLB:
+ case REG_ASRIDRHC:
+ case REG_ASRIDRLC:
+ case REG_ASR76K:
+ case REG_ASR56K:
+ case REG_ASRMCRA:
+ case REG_ASRFSTA:
+ case REG_ASRMCRB:
+ case REG_ASRFSTB:
+ case REG_ASRMCRC:
+ case REG_ASRFSTC:
+ case REG_ASRMCR1A:
+ case REG_ASRMCR1B:
+ case REG_ASRMCR1C:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool fsl_asrc_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_ASRSTR:
+ case REG_ASRDIA:
+ case REG_ASRDIB:
+ case REG_ASRDIC:
+ case REG_ASRDOA:
+ case REG_ASRDOB:
+ case REG_ASRDOC:
+ case REG_ASRFSTA:
+ case REG_ASRFSTB:
+ case REG_ASRFSTC:
+ case REG_ASRCFG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_ASRCTR:
+ case REG_ASRIER:
+ case REG_ASRCNCR:
+ case REG_ASRCFG:
+ case REG_ASRCSR:
+ case REG_ASRCDR1:
+ case REG_ASRCDR2:
+ case REG_ASRSTR:
+ case REG_ASRPM1:
+ case REG_ASRPM2:
+ case REG_ASRPM3:
+ case REG_ASRPM4:
+ case REG_ASRPM5:
+ case REG_ASRTFR1:
+ case REG_ASRCCR:
+ case REG_ASRDIA:
+ case REG_ASRDIB:
+ case REG_ASRDIC:
+ case REG_ASRIDRHA:
+ case REG_ASRIDRLA:
+ case REG_ASRIDRHB:
+ case REG_ASRIDRLB:
+ case REG_ASRIDRHC:
+ case REG_ASRIDRLC:
+ case REG_ASR76K:
+ case REG_ASR56K:
+ case REG_ASRMCRA:
+ case REG_ASRMCRB:
+ case REG_ASRMCRC:
+ case REG_ASRMCR1A:
+ case REG_ASRMCR1B:
+ case REG_ASRMCR1C:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static struct regmap_config fsl_asrc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .max_register = REG_ASRMCR1C,
+ .readable_reg = fsl_asrc_readable_reg,
+ .volatile_reg = fsl_asrc_volatile_reg,
+ .writeable_reg = fsl_asrc_writeable_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+/**
+ * Initialize ASRC registers with a default configurations
+ */
+static int fsl_asrc_init(struct fsl_asrc *asrc_priv)
+{
+ /* Halt ASRC internal FP when input FIFO needs data for pair A, B, C */
+ regmap_write(asrc_priv->regmap, REG_ASRCTR, ASRCTR_ASRCEN);
+
+ /* Disable interrupt by default */
+ regmap_write(asrc_priv->regmap, REG_ASRIER, 0x0);
+
+ /* Apply recommended settings for parameters from Reference Manual */
+ regmap_write(asrc_priv->regmap, REG_ASRPM1, 0x7fffff);
+ regmap_write(asrc_priv->regmap, REG_ASRPM2, 0x255555);
+ regmap_write(asrc_priv->regmap, REG_ASRPM3, 0xff7280);
+ regmap_write(asrc_priv->regmap, REG_ASRPM4, 0xff7280);
+ regmap_write(asrc_priv->regmap, REG_ASRPM5, 0xff7280);
+
+ /* Base address for task queue FIFO. Set to 0x7C */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRTFR1,
+ ASRTFR1_TF_BASE_MASK, ASRTFR1_TF_BASE(0xfc));
+
+ /* Set the processing clock for 76KHz to 133M */
+ regmap_write(asrc_priv->regmap, REG_ASR76K, 0x06D6);
+
+ /* Set the processing clock for 56KHz to 133M */
+ regmap_write(asrc_priv->regmap, REG_ASR56K, 0x0947);
+
+ return 0;
+}
+
+static int fsl_asrc_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(fsl_asrc_ids, &pdev->dev);
+ struct device_node *np = pdev->dev.of_node;
+ struct fsl_asrc *asrc_priv;
+ enum fsl_asrc_type devtype;
+ struct resource *res;
+ void __iomem *regs;
+ int irq, ret, i;
+ char tmp[16];
+
+ asrc_priv = devm_kzalloc(&pdev->dev, sizeof(*asrc_priv), GFP_KERNEL);
+ if (!asrc_priv)
+ return -ENOMEM;
+
+ if (of_id)
+ pdev->id_entry = of_id->data;
+ devtype = pdev->id_entry->driver_data;
+
+ switch (devtype) {
+ case IMX35_ASRC:
+ asrc_priv->channel_bits = 3;
+ clk_map[IN] = input_clk_map_imx35;
+ clk_map[OUT] = output_clk_map_imx35;
+ break;
+ case IMX53_ASRC:
+ asrc_priv->channel_bits = 4;
+ clk_map[IN] = input_clk_map_imx53;
+ clk_map[OUT] = output_clk_map_imx53;
+ break;
+ default:
+ dev_err(&pdev->dev, "unsupported device type\n");
+ return -EINVAL;
+ }
+
+ asrc_priv->pdev = pdev;
+ strcpy(asrc_priv->name, np->name);
+
+ /* Get the addresses and IRQ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ asrc_priv->paddr = res->start;
+
+ /* Register regmap and let it prepare core clock */
+ if (of_property_read_bool(np, "big-endian"))
+ fsl_asrc_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
+
+ asrc_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+ "mem", regs, &fsl_asrc_regmap_config);
+ if (IS_ERR(asrc_priv->regmap)) {
+ dev_err(&pdev->dev, "failed to init regmap\n");
+ return PTR_ERR(asrc_priv->regmap);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
+ return irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, fsl_asrc_isr, 0,
+ asrc_priv->name, asrc_priv);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret);
+ return ret;
+ }
+
+ asrc_priv->mem_clk = devm_clk_get(&pdev->dev, "mem");
+ if (IS_ERR(asrc_priv->mem_clk)) {
+ dev_err(&pdev->dev, "failed to get mem clock\n");
+ return PTR_ERR(asrc_priv->ipg_clk);
+ }
+
+ asrc_priv->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(asrc_priv->ipg_clk)) {
+ dev_err(&pdev->dev, "failed to get ipg clock\n");
+ return PTR_ERR(asrc_priv->ipg_clk);
+ }
+
+ for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
+ sprintf(tmp, "asrck_%x", i);
+ asrc_priv->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp);
+ if (IS_ERR(asrc_priv->asrck_clk[i])) {
+ dev_err(&pdev->dev, "failed to get %s clock\n", tmp);
+ return PTR_ERR(asrc_priv->asrck_clk[i]);
+ }
+ }
+
+ ret = fsl_asrc_init(asrc_priv);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to init asrc %d\n", ret);
+ return -EINVAL;
+ }
+
+ asrc_priv->channel_avail = 10;
+
+ ret = of_property_read_u32(np, "fsl,asrc-rate",
+ &asrc_priv->asrc_rate);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get output rate\n");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(np, "fsl,asrc-width",
+ &asrc_priv->asrc_width);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get output width\n");
+ return -EINVAL;
+ }
+
+ if (asrc_priv->asrc_width != 16 && asrc_priv->asrc_width != 24) {
+ dev_warn(&pdev->dev, "unsupported width, switching to 24bit\n");
+ asrc_priv->asrc_width = 24;
+ }
+
+ platform_set_drvdata(pdev, asrc_priv);
+ pm_runtime_enable(&pdev->dev);
+ spin_lock_init(&asrc_priv->lock);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component,
+ &fsl_asrc_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register ASoC DAI\n");
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_platform(&pdev->dev, &fsl_asrc_platform);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register ASoC platform\n");
+ return ret;
+ }
+
+ dev_info(&pdev->dev, "driver registered\n");
+
+ return 0;
+}
+
+#if CONFIG_PM_RUNTIME
+static int fsl_asrc_runtime_resume(struct device *dev)
+{
+ struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+ int i;
+
+ clk_prepare_enable(asrc_priv->mem_clk);
+ clk_prepare_enable(asrc_priv->ipg_clk);
+ for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
+ clk_prepare_enable(asrc_priv->asrck_clk[i]);
+
+ return 0;
+}
+
+static int fsl_asrc_runtime_suspend(struct device *dev)
+{
+ struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
+ clk_disable_unprepare(asrc_priv->asrck_clk[i]);
+ clk_disable_unprepare(asrc_priv->ipg_clk);
+ clk_disable_unprepare(asrc_priv->mem_clk);
+
+ return 0;
+}
+#endif /* CONFIG_PM_RUNTIME */
+
+#if CONFIG_PM_SLEEP
+static int fsl_asrc_suspend(struct device *dev)
+{
+ struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+
+ regcache_cache_only(asrc_priv->regmap, true);
+ regcache_mark_dirty(asrc_priv->regmap);
+
+ return 0;
+}
+
+static int fsl_asrc_resume(struct device *dev)
+{
+ struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+ u32 asrctr;
+
+ /* Stop all pairs provisionally */
+ regmap_read(asrc_priv->regmap, REG_ASRCTR, &asrctr);
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ ASRCTR_ASRCEi_ALL_MASK, 0);
+
+ /* Restore all registers */
+ regcache_cache_only(asrc_priv->regmap, false);
+ regcache_sync(asrc_priv->regmap);
+
+ /* Restart enabled pairs */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ ASRCTR_ASRCEi_ALL_MASK, asrctr);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_asrc_pm = {
+ SET_RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_asrc_suspend, fsl_asrc_resume)
+};
+
+static struct platform_driver fsl_asrc_driver = {
+ .probe = fsl_asrc_probe,
+ .driver = {
+ .name = "fsl-asrc",
+ .of_match_table = fsl_asrc_ids,
+ .pm = &fsl_asrc_pm,
+ },
+};
+module_platform_driver(fsl_asrc_driver);
+
+MODULE_DESCRIPTION("Freescale ASRC ASoC driver");
+MODULE_AUTHOR("Nicolin Chen <nicoleotsuka@gmail.com>");
+MODULE_ALIAS("platform:fsl-asrc");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h
new file mode 100644
index 0000000..a3f211f
--- /dev/null
+++ b/sound/soc/fsl/fsl_asrc.h
@@ -0,0 +1,461 @@
+/*
+ * fsl_asrc.h - Freescale ASRC ALSA SoC header file
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <nicoleotsuka@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef _FSL_ASRC_H
+#define _FSL_ASRC_H
+
+#define IN 0
+#define OUT 1
+
+#define ASRC_DMA_BUFFER_NUM 2
+#define ASRC_INPUTFIFO_THRESHOLD 32
+#define ASRC_OUTPUTFIFO_THRESHOLD 32
+#define ASRC_FIFO_THRESHOLD_MIN 0
+#define ASRC_FIFO_THRESHOLD_MAX 63
+#define ASRC_DMA_BUFFER_SIZE (1024 * 48 * 4)
+#define ASRC_MAX_BUFFER_SIZE (1024 * 48)
+#define ASRC_OUTPUT_LAST_SAMPLE 8
+
+#define IDEAL_RATIO_RATE 1000000
+
+#define REG_ASRCTR 0x00
+#define REG_ASRIER 0x04
+#define REG_ASRCNCR 0x0C
+#define REG_ASRCFG 0x10
+#define REG_ASRCSR 0x14
+
+#define REG_ASRCDR1 0x18
+#define REG_ASRCDR2 0x1C
+#define REG_ASRCDR(i) ((i < 2) ? REG_ASRCDR1 : REG_ASRCDR2)
+
+#define REG_ASRSTR 0x20
+#define REG_ASRRA 0x24
+#define REG_ASRRB 0x28
+#define REG_ASRRC 0x2C
+#define REG_ASRPM1 0x40
+#define REG_ASRPM2 0x44
+#define REG_ASRPM3 0x48
+#define REG_ASRPM4 0x4C
+#define REG_ASRPM5 0x50
+#define REG_ASRTFR1 0x54
+#define REG_ASRCCR 0x5C
+
+#define REG_ASRDIA 0x60
+#define REG_ASRDOA 0x64
+#define REG_ASRDIB 0x68
+#define REG_ASRDOB 0x6C
+#define REG_ASRDIC 0x70
+#define REG_ASRDOC 0x74
+#define REG_ASRDI(i) (REG_ASRDIA + (i << 3))
+#define REG_ASRDO(i) (REG_ASRDOA + (i << 3))
+#define REG_ASRDx(x, i) (x == IN ? REG_ASRDI(i) : REG_ASRDO(i))
+
+#define REG_ASRIDRHA 0x80
+#define REG_ASRIDRLA 0x84
+#define REG_ASRIDRHB 0x88
+#define REG_ASRIDRLB 0x8C
+#define REG_ASRIDRHC 0x90
+#define REG_ASRIDRLC 0x94
+#define REG_ASRIDRH(i) (REG_ASRIDRHA + (i << 3))
+#define REG_ASRIDRL(i) (REG_ASRIDRLA + (i << 3))
+
+#define REG_ASR76K 0x98
+#define REG_ASR56K 0x9C
+
+#define REG_ASRMCRA 0xA0
+#define REG_ASRFSTA 0xA4
+#define REG_ASRMCRB 0xA8
+#define REG_ASRFSTB 0xAC
+#define REG_ASRMCRC 0xB0
+#define REG_ASRFSTC 0xB4
+#define REG_ASRMCR(i) (REG_ASRMCRA + (i << 3))
+#define REG_ASRFST(i) (REG_ASRFSTA + (i << 3))
+
+#define REG_ASRMCR1A 0xC0
+#define REG_ASRMCR1B 0xC4
+#define REG_ASRMCR1C 0xC8
+#define REG_ASRMCR1(i) (REG_ASRMCR1A + (i << 2))
+
+
+/* REG0 0x00 REG_ASRCTR */
+#define ASRCTR_ATSi_SHIFT(i) (20 + i)
+#define ASRCTR_ATSi_MASK(i) (1 << ASRCTR_ATSi_SHIFT(i))
+#define ASRCTR_ATS(i) (1 << ASRCTR_ATSi_SHIFT(i))
+#define ASRCTR_USRi_SHIFT(i) (14 + (i << 1))
+#define ASRCTR_USRi_MASK(i) (1 << ASRCTR_USRi_SHIFT(i))
+#define ASRCTR_USR(i) (1 << ASRCTR_USRi_SHIFT(i))
+#define ASRCTR_IDRi_SHIFT(i) (13 + (i << 1))
+#define ASRCTR_IDRi_MASK(i) (1 << ASRCTR_IDRi_SHIFT(i))
+#define ASRCTR_IDR(i) (1 << ASRCTR_IDRi_SHIFT(i))
+#define ASRCTR_SRST_SHIFT 4
+#define ASRCTR_SRST_MASK (1 << ASRCTR_SRST_SHIFT)
+#define ASRCTR_SRST (1 << ASRCTR_SRST_SHIFT)
+#define ASRCTR_ASRCEi_SHIFT(i) (1 + i)
+#define ASRCTR_ASRCEi_MASK(i) (1 << ASRCTR_ASRCEi_SHIFT(i))
+#define ASRCTR_ASRCE(i) (1 << ASRCTR_ASRCEi_SHIFT(i))
+#define ASRCTR_ASRCEi_ALL_MASK (0x7 << ASRCTR_ASRCEi_SHIFT(0))
+#define ASRCTR_ASRCEN_SHIFT 0
+#define ASRCTR_ASRCEN_MASK (1 << ASRCTR_ASRCEN_SHIFT)
+#define ASRCTR_ASRCEN (1 << ASRCTR_ASRCEN_SHIFT)
+
+/* REG1 0x04 REG_ASRIER */
+#define ASRIER_AFPWE_SHIFT 7
+#define ASRIER_AFPWE_MASK (1 << ASRIER_AFPWE_SHIFT)
+#define ASRIER_AFPWE (1 << ASRIER_AFPWE_SHIFT)
+#define ASRIER_AOLIE_SHIFT 6
+#define ASRIER_AOLIE_MASK (1 << ASRIER_AOLIE_SHIFT)
+#define ASRIER_AOLIE (1 << ASRIER_AOLIE_SHIFT)
+#define ASRIER_ADOEi_SHIFT(i) (3 + i)
+#define ASRIER_ADOEi_MASK(i) (1 << ASRIER_ADOEi_SHIFT(i))
+#define ASRIER_ADOE(i) (1 << ASRIER_ADOEi_SHIFT(i))
+#define ASRIER_ADIEi_SHIFT(i) (0 + i)
+#define ASRIER_ADIEi_MASK(i) (1 << ASRIER_ADIEi_SHIFT(i))
+#define ASRIER_ADIE(i) (1 << ASRIER_ADIEi_SHIFT(i))
+
+/* REG2 0x0C REG_ASRCNCR */
+#define ASRCNCR_ANCi_SHIFT(i, b) (b * i)
+#define ASRCNCR_ANCi_MASK(i, b) (((1 << b) - 1) << ASRCNCR_ANCi_SHIFT(i, b))
+#define ASRCNCR_ANCi(i, v, b) ((v << ASRCNCR_ANCi_SHIFT(i, b)) & ASRCNCR_ANCi_MASK(i, b))
+
+/* REG3 0x10 REG_ASRCFG */
+#define ASRCFG_INIRQi_SHIFT(i) (21 + i)
+#define ASRCFG_INIRQi_MASK(i) (1 << ASRCFG_INIRQi_SHIFT(i))
+#define ASRCFG_INIRQi (1 << ASRCFG_INIRQi_SHIFT(i))
+#define ASRCFG_NDPRi_SHIFT(i) (18 + i)
+#define ASRCFG_NDPRi_MASK(i) (1 << ASRCFG_NDPRi_SHIFT(i))
+#define ASRCFG_NDPRi (1 << ASRCFG_NDPRi_SHIFT(i))
+#define ASRCFG_POSTMODi_SHIFT(i) (8 + (i << 2))
+#define ASRCFG_POSTMODi_WIDTH 2
+#define ASRCFG_POSTMODi_MASK(i) (((1 << ASRCFG_POSTMODi_WIDTH) - 1) << ASRCFG_POSTMODi_SHIFT(i))
+#define ASRCFG_POSTMOD(i, v) ((v) << ASRCFG_POSTMODi_SHIFT(i))
+#define ASRCFG_POSTMODi_UP(i) (0 << ASRCFG_POSTMODi_SHIFT(i))
+#define ASRCFG_POSTMODi_DCON(i) (1 << ASRCFG_POSTMODi_SHIFT(i))
+#define ASRCFG_POSTMODi_DOWN(i) (2 << ASRCFG_POSTMODi_SHIFT(i))
+#define ASRCFG_PREMODi_SHIFT(i) (6 + (i << 2))
+#define ASRCFG_PREMODi_WIDTH 2
+#define ASRCFG_PREMODi_MASK(i) (((1 << ASRCFG_PREMODi_WIDTH) - 1) << ASRCFG_PREMODi_SHIFT(i))
+#define ASRCFG_PREMOD(i, v) ((v) << ASRCFG_PREMODi_SHIFT(i))
+#define ASRCFG_PREMODi_UP(i) (0 << ASRCFG_PREMODi_SHIFT(i))
+#define ASRCFG_PREMODi_DCON(i) (1 << ASRCFG_PREMODi_SHIFT(i))
+#define ASRCFG_PREMODi_DOWN(i) (2 << ASRCFG_PREMODi_SHIFT(i))
+#define ASRCFG_PREMODi_BYPASS(i) (3 << ASRCFG_PREMODi_SHIFT(i))
+
+/* REG4 0x14 REG_ASRCSR */
+#define ASRCSR_AxCSi_WIDTH 4
+#define ASRCSR_AxCSi_MASK ((1 << ASRCSR_AxCSi_WIDTH) - 1)
+#define ASRCSR_AOCSi_SHIFT(i) (12 + (i << 2))
+#define ASRCSR_AOCSi_MASK(i) (((1 << ASRCSR_AxCSi_WIDTH) - 1) << ASRCSR_AOCSi_SHIFT(i))
+#define ASRCSR_AOCS(i, v) ((v) << ASRCSR_AOCSi_SHIFT(i))
+#define ASRCSR_AICSi_SHIFT(i) (i << 2)
+#define ASRCSR_AICSi_MASK(i) (((1 << ASRCSR_AxCSi_WIDTH) - 1) << ASRCSR_AICSi_SHIFT(i))
+#define ASRCSR_AICS(i, v) ((v) << ASRCSR_AICSi_SHIFT(i))
+
+/* REG5&6 0x18 & 0x1C REG_ASRCDR1 & ASRCDR2 */
+#define ASRCDRi_AxCPi_WIDTH 3
+#define ASRCDRi_AICPi_SHIFT(i) (0 + (i % 2) * 6)
+#define ASRCDRi_AICPi_MASK(i) (((1 << ASRCDRi_AxCPi_WIDTH) - 1) << ASRCDRi_AICPi_SHIFT(i))
+#define ASRCDRi_AICP(i, v) ((v) << ASRCDRi_AICPi_SHIFT(i))
+#define ASRCDRi_AICDi_SHIFT(i) (3 + (i % 2) * 6)
+#define ASRCDRi_AICDi_MASK(i) (((1 << ASRCDRi_AxCPi_WIDTH) - 1) << ASRCDRi_AICDi_SHIFT(i))
+#define ASRCDRi_AICD(i, v) ((v) << ASRCDRi_AICDi_SHIFT(i))
+#define ASRCDRi_AOCPi_SHIFT(i) ((i < 2) ? 12 + i * 6 : 6)
+#define ASRCDRi_AOCPi_MASK(i) (((1 << ASRCDRi_AxCPi_WIDTH) - 1) << ASRCDRi_AOCPi_SHIFT(i))
+#define ASRCDRi_AOCP(i, v) ((v) << ASRCDRi_AOCPi_SHIFT(i))
+#define ASRCDRi_AOCDi_SHIFT(i) ((i < 2) ? 15 + i * 6 : 9)
+#define ASRCDRi_AOCDi_MASK(i) (((1 << ASRCDRi_AxCPi_WIDTH) - 1) << ASRCDRi_AOCDi_SHIFT(i))
+#define ASRCDRi_AOCD(i, v) ((v) << ASRCDRi_AOCDi_SHIFT(i))
+
+/* REG7 0x20 REG_ASRSTR */
+#define ASRSTR_DSLCNT_SHIFT 21
+#define ASRSTR_DSLCNT_MASK (1 << ASRSTR_DSLCNT_SHIFT)
+#define ASRSTR_DSLCNT (1 << ASRSTR_DSLCNT_SHIFT)
+#define ASRSTR_ATQOL_SHIFT 20
+#define ASRSTR_ATQOL_MASK (1 << ASRSTR_ATQOL_SHIFT)
+#define ASRSTR_ATQOL (1 << ASRSTR_ATQOL_SHIFT)
+#define ASRSTR_AOOLi_SHIFT(i) (17 + i)
+#define ASRSTR_AOOLi_MASK(i) (1 << ASRSTR_AOOLi_SHIFT(i))
+#define ASRSTR_AOOL(i) (1 << ASRSTR_AOOLi_SHIFT(i))
+#define ASRSTR_AIOLi_SHIFT(i) (14 + i)
+#define ASRSTR_AIOLi_MASK(i) (1 << ASRSTR_AIOLi_SHIFT(i))
+#define ASRSTR_AIOL(i) (1 << ASRSTR_AIOLi_SHIFT(i))
+#define ASRSTR_AODOi_SHIFT(i) (11 + i)
+#define ASRSTR_AODOi_MASK(i) (1 << ASRSTR_AODOi_SHIFT(i))
+#define ASRSTR_AODO(i) (1 << ASRSTR_AODOi_SHIFT(i))
+#define ASRSTR_AIDUi_SHIFT(i) (8 + i)
+#define ASRSTR_AIDUi_MASK(i) (1 << ASRSTR_AIDUi_SHIFT(i))
+#define ASRSTR_AIDU(i) (1 << ASRSTR_AIDUi_SHIFT(i))
+#define ASRSTR_FPWT_SHIFT 7
+#define ASRSTR_FPWT_MASK (1 << ASRSTR_FPWT_SHIFT)
+#define ASRSTR_FPWT (1 << ASRSTR_FPWT_SHIFT)
+#define ASRSTR_AOLE_SHIFT 6
+#define ASRSTR_AOLE_MASK (1 << ASRSTR_AOLE_SHIFT)
+#define ASRSTR_AOLE (1 << ASRSTR_AOLE_SHIFT)
+#define ASRSTR_AODEi_SHIFT(i) (3 + i)
+#define ASRSTR_AODFi_MASK(i) (1 << ASRSTR_AODEi_SHIFT(i))
+#define ASRSTR_AODF(i) (1 << ASRSTR_AODEi_SHIFT(i))
+#define ASRSTR_AIDEi_SHIFT(i) (0 + i)
+#define ASRSTR_AIDEi_MASK(i) (1 << ASRSTR_AIDEi_SHIFT(i))
+#define ASRSTR_AIDE(i) (1 << ASRSTR_AIDEi_SHIFT(i))
+
+/* REG10 0x54 REG_ASRTFR1 */
+#define ASRTFR1_TF_BASE_WIDTH 7
+#define ASRTFR1_TF_BASE_SHIFT 6
+#define ASRTFR1_TF_BASE_MASK (((1 << ASRTFR1_TF_BASE_WIDTH) - 1) << ASRTFR1_TF_BASE_SHIFT)
+#define ASRTFR1_TF_BASE(i) ((i) << ASRTFR1_TF_BASE_SHIFT)
+
+/*
+ * REG22 0xA0 REG_ASRMCRA
+ * REG24 0xA8 REG_ASRMCRB
+ * REG26 0xB0 REG_ASRMCRC
+ */
+#define ASRMCRi_ZEROBUFi_SHIFT 23
+#define ASRMCRi_ZEROBUFi_MASK (1 << ASRMCRi_ZEROBUFi_SHIFT)
+#define ASRMCRi_ZEROBUFi (1 << ASRMCRi_ZEROBUFi_SHIFT)
+#define ASRMCRi_EXTTHRSHi_SHIFT 22
+#define ASRMCRi_EXTTHRSHi_MASK (1 << ASRMCRi_EXTTHRSHi_SHIFT)
+#define ASRMCRi_EXTTHRSHi (1 << ASRMCRi_EXTTHRSHi_SHIFT)
+#define ASRMCRi_BUFSTALLi_SHIFT 21
+#define ASRMCRi_BUFSTALLi_MASK (1 << ASRMCRi_BUFSTALLi_SHIFT)
+#define ASRMCRi_BUFSTALLi (1 << ASRMCRi_BUFSTALLi_SHIFT)
+#define ASRMCRi_BYPASSPOLYi_SHIFT 20
+#define ASRMCRi_BYPASSPOLYi_MASK (1 << ASRMCRi_BYPASSPOLYi_SHIFT)
+#define ASRMCRi_BYPASSPOLYi (1 << ASRMCRi_BYPASSPOLYi_SHIFT)
+#define ASRMCRi_OUTFIFO_THRESHOLD_WIDTH 6
+#define ASRMCRi_OUTFIFO_THRESHOLD_SHIFT 12
+#define ASRMCRi_OUTFIFO_THRESHOLD_MASK (((1 << ASRMCRi_OUTFIFO_THRESHOLD_WIDTH) - 1) << ASRMCRi_OUTFIFO_THRESHOLD_SHIFT)
+#define ASRMCRi_OUTFIFO_THRESHOLD(v) (((v) << ASRMCRi_OUTFIFO_THRESHOLD_SHIFT) & ASRMCRi_OUTFIFO_THRESHOLD_MASK)
+#define ASRMCRi_RSYNIFi_SHIFT 11
+#define ASRMCRi_RSYNIFi_MASK (1 << ASRMCRi_RSYNIFi_SHIFT)
+#define ASRMCRi_RSYNIFi (1 << ASRMCRi_RSYNIFi_SHIFT)
+#define ASRMCRi_RSYNOFi_SHIFT 10
+#define ASRMCRi_RSYNOFi_MASK (1 << ASRMCRi_RSYNOFi_SHIFT)
+#define ASRMCRi_RSYNOFi (1 << ASRMCRi_RSYNOFi_SHIFT)
+#define ASRMCRi_INFIFO_THRESHOLD_WIDTH 6
+#define ASRMCRi_INFIFO_THRESHOLD_SHIFT 0
+#define ASRMCRi_INFIFO_THRESHOLD_MASK (((1 << ASRMCRi_INFIFO_THRESHOLD_WIDTH) - 1) << ASRMCRi_INFIFO_THRESHOLD_SHIFT)
+#define ASRMCRi_INFIFO_THRESHOLD(v) (((v) << ASRMCRi_INFIFO_THRESHOLD_SHIFT) & ASRMCRi_INFIFO_THRESHOLD_MASK)
+
+/*
+ * REG23 0xA4 REG_ASRFSTA
+ * REG25 0xAC REG_ASRFSTB
+ * REG27 0xB4 REG_ASRFSTC
+ */
+#define ASRFSTi_OAFi_SHIFT 23
+#define ASRFSTi_OAFi_MASK (1 << ASRFSTi_OAFi_SHIFT)
+#define ASRFSTi_OAFi (1 << ASRFSTi_OAFi_SHIFT)
+#define ASRFSTi_OUTPUT_FIFO_WIDTH 7
+#define ASRFSTi_OUTPUT_FIFO_SHIFT 12
+#define ASRFSTi_OUTPUT_FIFO_MASK (((1 << ASRFSTi_OUTPUT_FIFO_WIDTH) - 1) << ASRFSTi_OUTPUT_FIFO_SHIFT)
+#define ASRFSTi_IAEi_SHIFT 11
+#define ASRFSTi_IAEi_MASK (1 << ASRFSTi_OAFi_SHIFT)
+#define ASRFSTi_IAEi (1 << ASRFSTi_OAFi_SHIFT)
+#define ASRFSTi_INPUT_FIFO_WIDTH 7
+#define ASRFSTi_INPUT_FIFO_SHIFT 0
+#define ASRFSTi_INPUT_FIFO_MASK ((1 << ASRFSTi_INPUT_FIFO_WIDTH) - 1)
+
+/* REG28 0xC0 & 0xC4 & 0xC8 REG_ASRMCR1i */
+#define ASRMCR1i_IWD_WIDTH 3
+#define ASRMCR1i_IWD_SHIFT 9
+#define ASRMCR1i_IWD_MASK (((1 << ASRMCR1i_IWD_WIDTH) - 1) << ASRMCR1i_IWD_SHIFT)
+#define ASRMCR1i_IWD(v) ((v) << ASRMCR1i_IWD_SHIFT)
+#define ASRMCR1i_IMSB_SHIFT 8
+#define ASRMCR1i_IMSB_MASK (1 << ASRMCR1i_IMSB_SHIFT)
+#define ASRMCR1i_IMSB_MSB (1 << ASRMCR1i_IMSB_SHIFT)
+#define ASRMCR1i_IMSB_LSB (0 << ASRMCR1i_IMSB_SHIFT)
+#define ASRMCR1i_OMSB_SHIFT 2
+#define ASRMCR1i_OMSB_MASK (1 << ASRMCR1i_OMSB_SHIFT)
+#define ASRMCR1i_OMSB_MSB (1 << ASRMCR1i_OMSB_SHIFT)
+#define ASRMCR1i_OMSB_LSB (0 << ASRMCR1i_OMSB_SHIFT)
+#define ASRMCR1i_OSGN_SHIFT 1
+#define ASRMCR1i_OSGN_MASK (1 << ASRMCR1i_OSGN_SHIFT)
+#define ASRMCR1i_OSGN (1 << ASRMCR1i_OSGN_SHIFT)
+#define ASRMCR1i_OW16_SHIFT 0
+#define ASRMCR1i_OW16_MASK (1 << ASRMCR1i_OW16_SHIFT)
+#define ASRMCR1i_OW16(v) ((v) << ASRMCR1i_OW16_SHIFT)
+
+
+enum asrc_pair_index {
+ ASRC_INVALID_PAIR = -1,
+ ASRC_PAIR_A = 0,
+ ASRC_PAIR_B = 1,
+ ASRC_PAIR_C = 2,
+};
+
+#define ASRC_PAIR_MAX_NUM (ASRC_PAIR_C + 1)
+
+enum asrc_inclk {
+ INCLK_NONE = 0x03,
+ INCLK_ESAI_RX = 0x00,
+ INCLK_SSI1_RX = 0x01,
+ INCLK_SSI2_RX = 0x02,
+ INCLK_SSI3_RX = 0x07,
+ INCLK_SPDIF_RX = 0x04,
+ INCLK_MLB_CLK = 0x05,
+ INCLK_PAD = 0x06,
+ INCLK_ESAI_TX = 0x08,
+ INCLK_SSI1_TX = 0x09,
+ INCLK_SSI2_TX = 0x0a,
+ INCLK_SSI3_TX = 0x0b,
+ INCLK_SPDIF_TX = 0x0c,
+ INCLK_ASRCK1_CLK = 0x0f,
+};
+
+enum asrc_outclk {
+ OUTCLK_NONE = 0x03,
+ OUTCLK_ESAI_TX = 0x00,
+ OUTCLK_SSI1_TX = 0x01,
+ OUTCLK_SSI2_TX = 0x02,
+ OUTCLK_SSI3_TX = 0x07,
+ OUTCLK_SPDIF_TX = 0x04,
+ OUTCLK_MLB_CLK = 0x05,
+ OUTCLK_PAD = 0x06,
+ OUTCLK_ESAI_RX = 0x08,
+ OUTCLK_SSI1_RX = 0x09,
+ OUTCLK_SSI2_RX = 0x0a,
+ OUTCLK_SSI3_RX = 0x0b,
+ OUTCLK_SPDIF_RX = 0x0c,
+ OUTCLK_ASRCK1_CLK = 0x0f,
+};
+
+#define ASRC_CLK_MAX_NUM 16
+
+enum asrc_word_width {
+ ASRC_WIDTH_24_BIT = 0,
+ ASRC_WIDTH_16_BIT = 1,
+ ASRC_WIDTH_8_BIT = 2,
+};
+
+struct asrc_config {
+ enum asrc_pair_index pair;
+ unsigned int channel_num;
+ unsigned int buffer_num;
+ unsigned int dma_buffer_size;
+ unsigned int input_sample_rate;
+ unsigned int output_sample_rate;
+ enum asrc_word_width input_word_width;
+ enum asrc_word_width output_word_width;
+ enum asrc_inclk inclk;
+ enum asrc_outclk outclk;
+};
+
+struct asrc_req {
+ unsigned int chn_num;
+ enum asrc_pair_index index;
+};
+
+struct asrc_querybuf {
+ unsigned int buffer_index;
+ unsigned int input_length;
+ unsigned int output_length;
+ unsigned long input_offset;
+ unsigned long output_offset;
+};
+
+struct asrc_convert_buffer {
+ void *input_buffer_vaddr;
+ void *output_buffer_vaddr;
+ unsigned int input_buffer_length;
+ unsigned int output_buffer_length;
+};
+
+struct asrc_status_flags {
+ enum asrc_pair_index index;
+ unsigned int overload_error;
+};
+
+enum asrc_error_status {
+ ASRC_TASK_Q_OVERLOAD = 0x01,
+ ASRC_OUTPUT_TASK_OVERLOAD = 0x02,
+ ASRC_INPUT_TASK_OVERLOAD = 0x04,
+ ASRC_OUTPUT_BUFFER_OVERFLOW = 0x08,
+ ASRC_INPUT_BUFFER_UNDERRUN = 0x10,
+};
+
+struct dma_block {
+ dma_addr_t dma_paddr;
+ void *dma_vaddr;
+ unsigned int length;
+};
+
+/**
+ * fsl_asrc_pair: ASRC Pair private data
+ *
+ * @asrc_priv: pointer to its parent module
+ * @config: configuration profile
+ * @error: error record
+ * @index: pair index (ASRC_PAIR_A, ASRC_PAIR_B, ASRC_PAIR_C)
+ * @channels: occupied channel number
+ * @desc: input and output dma descriptors
+ * @dma_chan: inputer and output DMA channels
+ * @dma_data: private dma data
+ * @pos: hardware pointer position
+ * @private: pair private area
+ */
+struct fsl_asrc_pair {
+ struct fsl_asrc *asrc_priv;
+ struct asrc_config *config;
+ unsigned int error;
+
+ enum asrc_pair_index index;
+ unsigned int channels;
+
+ struct dma_async_tx_descriptor *desc[2];
+ struct dma_chan *dma_chan[2];
+ struct imx_dma_data dma_data;
+ unsigned int pos;
+
+ void *private;
+};
+
+/**
+ * fsl_asrc_pair: ASRC private data
+ *
+ * @dma_params_rx: DMA parameters for receive channel
+ * @dma_params_tx: DMA parameters for transmit channel
+ * @pdev: platform device pointer
+ * @regmap: regmap handler
+ * @paddr: physical address to the base address of registers
+ * @mem_clk: clock source to access register
+ * @ipg_clk: clock source to drive peripheral
+ * @asrck_clk: clock sources to driver ASRC internal logic
+ * @lock: spin lock for resource protection
+ * @pair: pair pointers
+ * @channel_bits: width of ASRCNCR register for each pair
+ * @channel_avail: non-occupied channel numbers
+ * @asrc_rate: default sample rate for ASoC Back-Ends
+ * @asrc_width: default sample width for ASoC Back-Ends
+ * @name: driver name
+ */
+struct fsl_asrc {
+ struct snd_dmaengine_dai_dma_data dma_params_rx;
+ struct snd_dmaengine_dai_dma_data dma_params_tx;
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ unsigned long paddr;
+ struct clk *mem_clk;
+ struct clk *ipg_clk;
+ struct clk *asrck_clk[ASRC_CLK_MAX_NUM];
+ spinlock_t lock;
+
+ struct fsl_asrc_pair *pair[ASRC_PAIR_MAX_NUM];
+ unsigned int channel_bits;
+ unsigned int channel_avail;
+
+ int asrc_rate;
+ int asrc_width;
+
+ char name[32];
+};
+
+extern struct snd_soc_platform_driver fsl_asrc_platform;
+struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir);
+#endif /* _FSL_ASRC_H */
diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
new file mode 100644
index 0000000..5b1e73e
--- /dev/null
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -0,0 +1,386 @@
+/*
+ * Freescale ASRC ALSA SoC Platform (DMA) driver
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <nicoleotsuka@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/platform_data/dma-imx.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#include "fsl_asrc.h"
+
+#define FSL_ASRC_DMABUF_SIZE (256 * 1024)
+
+static struct snd_pcm_hardware snd_imx_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
+ .buffer_bytes_max = FSL_ASRC_DMABUF_SIZE,
+ .period_bytes_min = 128,
+ .period_bytes_max = 65535, /* Limited by SDMA engine */
+ .periods_min = 2,
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+static bool filter(struct dma_chan *chan, void *param)
+{
+ if (!imx_dma_is_general_purpose(chan))
+ return false;
+
+ chan->private = param;
+
+ return true;
+}
+
+static void fsl_asrc_dma_complete(void *arg)
+{
+ struct snd_pcm_substream *substream = arg;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+
+ pair->pos += snd_pcm_lib_period_bytes(substream);
+ if (pair->pos >= snd_pcm_lib_buffer_bytes(substream))
+ pair->pos = 0;
+
+ snd_pcm_period_elapsed(substream);
+}
+
+static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream)
+{
+ u8 dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? OUT : IN;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+ struct device *dev = rtd->platform->dev;
+ unsigned long flags = DMA_CTRL_ACK;
+
+ /* Prepare and submit Front-End DMA channel */
+ if (!substream->runtime->no_period_wakeup)
+ flags |= DMA_PREP_INTERRUPT;
+
+ pair->pos = 0;
+ pair->desc[!dir] = dmaengine_prep_dma_cyclic(
+ pair->dma_chan[!dir], runtime->dma_addr,
+ snd_pcm_lib_buffer_bytes(substream),
+ snd_pcm_lib_period_bytes(substream),
+ dir == OUT ? DMA_TO_DEVICE : DMA_FROM_DEVICE, flags);
+ if (!pair->desc[!dir]) {
+ dev_err(dev, "failed to prepare slave DMA for Front-End\n");
+ return -ENOMEM;
+ }
+
+ pair->desc[!dir]->callback = fsl_asrc_dma_complete;
+ pair->desc[!dir]->callback_param = substream;
+
+ dmaengine_submit(pair->desc[!dir]);
+
+ /* Prepare and submit Back-End DMA channel */
+ pair->desc[dir] = dmaengine_prep_dma_cyclic(
+ pair->dma_chan[dir], 0xffff, 64, 64, DMA_DEV_TO_DEV, 0);
+ if (!pair->desc[dir]) {
+ dev_err(dev, "failed to prepare slave DMA for Back-End\n");
+ return -ENOMEM;
+ }
+
+ dmaengine_submit(pair->desc[dir]);
+
+ return 0;
+}
+
+static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+ int ret;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = fsl_asrc_dma_prepare_and_submit(substream);
+ if (ret)
+ return ret;
+ dma_async_issue_pending(pair->dma_chan[IN]);
+ dma_async_issue_pending(pair->dma_chan[OUT]);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ dmaengine_terminate_all(pair->dma_chan[OUT]);
+ dmaengine_terminate_all(pair->dma_chan[IN]);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ struct snd_dmaengine_dai_dma_data *dma_params_fe = NULL;
+ struct snd_dmaengine_dai_dma_data *dma_params_be = NULL;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+ struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ struct dma_slave_config config_fe, config_be;
+ enum asrc_pair_index index = pair->index;
+ struct device *dev = rtd->platform->dev;
+ int stream = substream->stream;
+ struct imx_dma_data *tmp_data;
+ struct snd_soc_dpcm *dpcm;
+ struct dma_chan *tmp_chan;
+ struct device *dev_be;
+ u8 dir = tx ? OUT : IN;
+ dma_cap_mask_t mask;
+ int ret;
+
+ /* Fetch the Back-End dma_data from DPCM */
+ list_for_each_entry(dpcm, &rtd->dpcm[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ struct snd_pcm_substream *substream_be;
+ struct snd_soc_dai *dai = be->cpu_dai;
+
+ if (dpcm->fe != rtd)
+ continue;
+
+ substream_be = snd_soc_dpcm_get_substream(be, stream);
+ dma_params_be = snd_soc_dai_get_dma_data(dai, substream_be);
+ dev_be = dai->dev;
+ break;
+ }
+
+ if (!dma_params_be) {
+ dev_err(dev, "failed to get the substream of Back-End\n");
+ return -EINVAL;
+ }
+
+ /* Override dma_data of the Front-End and config its dmaengine */
+ dma_params_fe = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ dma_params_fe->addr = asrc_priv->paddr + REG_ASRDx(!dir, index);
+ dma_params_fe->maxburst = dma_params_be->maxburst;
+
+ pair->dma_chan[!dir] = fsl_asrc_get_dma_channel(pair, !dir);
+ if (!pair->dma_chan[!dir]) {
+ dev_err(dev, "failed to request DMA channel\n");
+ return -EINVAL;
+ }
+
+ memset(&config_fe, 0, sizeof(config_fe));
+ ret = snd_dmaengine_pcm_prepare_slave_config(substream, params, &config_fe);
+ if (ret) {
+ dev_err(dev, "failed to prepare DMA config for Front-End\n");
+ return ret;
+ }
+
+ ret = dmaengine_slave_config(pair->dma_chan[!dir], &config_fe);
+ if (ret) {
+ dev_err(dev, "failed to config DMA channel for Front-End\n");
+ return ret;
+ }
+
+ /* Request and config DMA channel for Back-End */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_set(DMA_CYCLIC, mask);
+
+ /* Get DMA request of Back-End */
+ tmp_chan = dma_request_slave_channel(dev_be, tx ? "tx" : "rx");
+ tmp_data = tmp_chan->private;
+ pair->dma_data.dma_request = tmp_data->dma_request;
+ dma_release_channel(tmp_chan);
+
+ /* Get DMA request of Front-End */
+ tmp_chan = fsl_asrc_get_dma_channel(pair, dir);
+ tmp_data = tmp_chan->private;
+ pair->dma_data.dma_request2 = tmp_data->dma_request;
+ pair->dma_data.peripheral_type = tmp_data->peripheral_type;
+ pair->dma_data.priority = tmp_data->priority;
+ dma_release_channel(tmp_chan);
+
+ pair->dma_chan[dir] = dma_request_channel(mask, filter, &pair->dma_data);
+ if (!pair->dma_chan[dir]) {
+ dev_err(dev, "failed to request DMA channel for Back-End\n");
+ return -EINVAL;
+ }
+
+ if (asrc_priv->asrc_width == 16)
+ buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ else
+ buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+ config_be.direction = DMA_DEV_TO_DEV;
+ config_be.src_addr_width = buswidth;
+ config_be.src_maxburst = dma_params_be->maxburst;
+ config_be.dst_addr_width = buswidth;
+ config_be.dst_maxburst = dma_params_be->maxburst;
+
+ if (tx) {
+ config_be.src_addr = asrc_priv->paddr + REG_ASRDO(index);
+ config_be.dst_addr = dma_params_be->addr;
+ } else {
+ config_be.dst_addr = asrc_priv->paddr + REG_ASRDI(index);
+ config_be.src_addr = dma_params_be->addr;
+ }
+
+ ret = dmaengine_slave_config(pair->dma_chan[dir], &config_be);
+ if (ret) {
+ dev_err(dev, "failed to config DMA channel for Back-End\n");
+ return ret;
+ }
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ return 0;
+}
+
+static int fsl_asrc_dma_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+
+ snd_pcm_set_runtime_buffer(substream, NULL);
+
+ if (pair->dma_chan[IN])
+ dma_release_channel(pair->dma_chan[IN]);
+
+ if (pair->dma_chan[OUT])
+ dma_release_channel(pair->dma_chan[OUT]);
+
+ pair->dma_chan[IN] = NULL;
+ pair->dma_chan[OUT] = NULL;
+
+ return 0;
+}
+
+static int fsl_asrc_dma_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct device *dev = rtd->platform->dev;
+ struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+ struct fsl_asrc_pair *pair;
+
+ pair = kzalloc(sizeof(struct fsl_asrc_pair), GFP_KERNEL);
+ if (!pair) {
+ dev_err(dev, "failed to allocate pair\n");
+ return -ENOMEM;
+ }
+
+ pair->asrc_priv = asrc_priv;
+
+ runtime->private_data = pair;
+
+ snd_pcm_hw_constraint_integer(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
+
+ return 0;
+}
+
+static int fsl_asrc_dma_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+ struct fsl_asrc *asrc_priv = pair->asrc_priv;
+
+ if (pair && asrc_priv->pair[pair->index] == pair)
+ asrc_priv->pair[pair->index] = NULL;
+
+ kfree(pair);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t fsl_asrc_dma_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+
+ return bytes_to_frames(substream->runtime, pair->pos);
+}
+
+static struct snd_pcm_ops fsl_asrc_dma_pcm_ops = {
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = fsl_asrc_dma_hw_params,
+ .hw_free = fsl_asrc_dma_hw_free,
+ .trigger = fsl_asrc_dma_trigger,
+ .open = fsl_asrc_dma_startup,
+ .close = fsl_asrc_dma_shutdown,
+ .pointer = fsl_asrc_dma_pcm_pointer,
+};
+
+static int fsl_asrc_dma_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm *pcm = rtd->pcm;
+ int ret, i;
+
+ ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(card->dev, "failed to set DMA mask\n");
+ return ret;
+ }
+
+ for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) {
+ substream = pcm->streams[i].substream;
+ if (!substream)
+ continue;
+
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev,
+ FSL_ASRC_DMABUF_SIZE, &substream->dma_buffer);
+ if (ret) {
+ dev_err(card->dev, "failed to allocate DMA buffer\n");
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ if (--i == 0 && pcm->streams[i].substream)
+ snd_dma_free_pages(&pcm->streams[i].substream->dma_buffer);
+
+ return ret;
+}
+
+static void fsl_asrc_dma_pcm_free(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ int i;
+
+ for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) {
+ substream = pcm->streams[i].substream;
+ if (!substream)
+ continue;
+
+ snd_dma_free_pages(&substream->dma_buffer);
+ substream->dma_buffer.area = NULL;
+ substream->dma_buffer.addr = 0;
+ }
+}
+
+struct snd_soc_platform_driver fsl_asrc_platform = {
+ .ops = &fsl_asrc_dma_pcm_ops,
+ .pcm_new = fsl_asrc_dma_pcm_new,
+ .pcm_free = fsl_asrc_dma_pcm_free,
+};
+EXPORT_SYMBOL_GPL(fsl_asrc_platform);
--
1.8.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox