* [PATCH v4 09/16] powerpc/iommu: Fix IOMMU ownership control functions
From: Alexey Kardashevskiy @ 2014-07-30 9:31 UTC (permalink / raw)
To: linuxppc-dev
Cc: Alexey Kardashevskiy, Michael Ellerman, Paul Mackerras,
Gavin Shan
In-Reply-To: <1406712695-9491-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.
Clearing must be done outside of the table locks as iommu_clear_tce()
called from iommu_clear_tces_and_put_pages() does this.
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
arch/powerpc/kernel/iommu.c | 36 +++++++++++++++++++++++++++++-------
1 file changed, 29 insertions(+), 7 deletions(-)
diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
index 06984d5..c94b11d 100644
--- a/arch/powerpc/kernel/iommu.c
+++ b/arch/powerpc/kernel/iommu.c
@@ -1103,33 +1103,55 @@ 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);
+ for (i = 0; i < tbl->nr_pools; i++)
+ spin_unlock(&tbl->pools[i].lock);
+ spin_unlock_irqrestore(&tbl->large_pool.lock, flags);
- return 0;
+ if (!ret)
+ iommu_clear_tces_and_put_pages(tbl, tbl->it_offset,
+ tbl->it_size);
+ 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 v4 10/16] powerpc: Move tce_xxx callbacks from ppc_md to iommu_table
From: Alexey Kardashevskiy @ 2014-07-30 9:31 UTC (permalink / raw)
To: linuxppc-dev
Cc: Alexey Kardashevskiy, Michael Ellerman, Paul Mackerras,
Gavin Shan
In-Reply-To: <1406712695-9491-1-git-send-email-aik@ozlabs.ru>
This adds a iommu_table_ops struct and puts pointer to it into
the iommu_table struct. This moves tce_build/tce_free/tce_get/tce_flush
callbacks from ppc_md to the new struct where they really belong to.
This adds an extra @ops parameter to iommu_init_table() to make sure
that we do not leave any IOMMU table without iommu_table_ops. @it_ops is
initialized in the very beginning as iommu_init_table() calls
iommu_table_clear() and the latter uses callbacks already.
This does s/tce_build/set/, s/tce_free/clear/ and removes "tce_" prefixes
for better readability.
This removes tce_xxx_rm handlers from ppc_md as well but does not add
them to iommu_table_ops, this will be done later if we decide to support
TCE hypercalls in real mode.
This always uses tce_buildmulti_pSeriesLP/tce_buildmulti_pSeriesLP as
callbacks for pseries. This changes "multi" callbacks to fall back to
tce_build_pSeriesLP/tce_free_pSeriesLP if FW_FEATURE_MULTITCE is not
present. The reason for this is we still have to support "multitce=off"
boot parameter in disable_multitce() and we do not want to walk through
all IOMMU tables in the system and replace "multi" callbacks with single
ones.
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
arch/powerpc/include/asm/iommu.h | 20 +++++++++++-
arch/powerpc/include/asm/machdep.h | 25 ---------------
arch/powerpc/kernel/iommu.c | 50 ++++++++++++++++-------------
arch/powerpc/kernel/vio.c | 5 ++-
arch/powerpc/platforms/cell/iommu.c | 9 ++++--
arch/powerpc/platforms/pasemi/iommu.c | 8 +++--
arch/powerpc/platforms/powernv/pci-ioda.c | 4 +--
arch/powerpc/platforms/powernv/pci-p5ioc2.c | 3 +-
arch/powerpc/platforms/powernv/pci.c | 24 ++++----------
arch/powerpc/platforms/powernv/pci.h | 1 +
arch/powerpc/platforms/pseries/iommu.c | 42 +++++++++++++-----------
arch/powerpc/sysdev/dart_iommu.c | 13 ++++----
12 files changed, 102 insertions(+), 102 deletions(-)
diff --git a/arch/powerpc/include/asm/iommu.h b/arch/powerpc/include/asm/iommu.h
index 2b0b01d..c725e4a 100644
--- a/arch/powerpc/include/asm/iommu.h
+++ b/arch/powerpc/include/asm/iommu.h
@@ -43,6 +43,22 @@
extern int iommu_is_off;
extern int iommu_force_on;
+struct iommu_table_ops {
+ int (*set)(struct iommu_table *tbl,
+ long index, long npages,
+ unsigned long uaddr,
+ enum dma_data_direction direction,
+ struct dma_attrs *attrs);
+ void (*clear)(struct iommu_table *tbl,
+ long index, long npages);
+ unsigned long (*get)(struct iommu_table *tbl, long index);
+ void (*flush)(struct iommu_table *tbl);
+};
+
+/* These are used by VIO */
+extern struct iommu_table_ops iommu_table_lpar_multi_ops;
+extern struct iommu_table_ops iommu_table_pseries_ops;
+
/*
* IOMAP_MAX_ORDER defines the largest contiguous block
* of dma space we can get. IOMAP_MAX_ORDER = 13
@@ -77,6 +93,7 @@ struct iommu_table {
#ifdef CONFIG_IOMMU_API
struct iommu_group *it_group;
#endif
+ struct iommu_table_ops *it_ops;
};
/* Pure 2^n version of get_order */
@@ -106,7 +123,8 @@ extern void iommu_free_table(struct iommu_table *tbl, const char *node_name);
* structure
*/
extern struct iommu_table *iommu_init_table(struct iommu_table * tbl,
- int nid);
+ int nid,
+ struct iommu_table_ops *ops);
struct spapr_tce_iommu_ops;
#ifdef CONFIG_IOMMU_API
diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h
index f92b0b5..0a2ec04 100644
--- a/arch/powerpc/include/asm/machdep.h
+++ b/arch/powerpc/include/asm/machdep.h
@@ -65,31 +65,6 @@ struct machdep_calls {
* destroyed as well */
void (*hpte_clear_all)(void);
- int (*tce_build)(struct iommu_table *tbl,
- long index,
- long npages,
- unsigned long uaddr,
- enum dma_data_direction direction,
- struct dma_attrs *attrs);
- void (*tce_free)(struct iommu_table *tbl,
- long index,
- long npages);
- unsigned long (*tce_get)(struct iommu_table *tbl,
- long index);
- void (*tce_flush)(struct iommu_table *tbl);
-
- /* _rm versions are for real mode use only */
- int (*tce_build_rm)(struct iommu_table *tbl,
- long index,
- long npages,
- unsigned long uaddr,
- enum dma_data_direction direction,
- struct dma_attrs *attrs);
- void (*tce_free_rm)(struct iommu_table *tbl,
- long index,
- long npages);
- void (*tce_flush_rm)(struct iommu_table *tbl);
-
void __iomem * (*ioremap)(phys_addr_t addr, unsigned long size,
unsigned long flags, void *caller);
void (*iounmap)(volatile void __iomem *token);
diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
index c94b11d..6a86788 100644
--- a/arch/powerpc/kernel/iommu.c
+++ b/arch/powerpc/kernel/iommu.c
@@ -322,11 +322,11 @@ static dma_addr_t iommu_alloc(struct device *dev, struct iommu_table *tbl,
ret = entry << tbl->it_page_shift; /* Set the return dma address */
/* Put the TCEs in the HW table */
- build_fail = ppc_md.tce_build(tbl, entry, npages,
+ build_fail = tbl->it_ops->set(tbl, entry, npages,
(unsigned long)page &
IOMMU_PAGE_MASK(tbl), direction, attrs);
- /* ppc_md.tce_build() only returns non-zero for transient errors.
+ /* tbl->it_ops->set() only returns non-zero for transient errors.
* Clean up the table bitmap in this case and return
* DMA_ERROR_CODE. For all other errors the functionality is
* not altered.
@@ -337,8 +337,8 @@ static dma_addr_t iommu_alloc(struct device *dev, struct iommu_table *tbl,
}
/* Flush/invalidate TLB caches if necessary */
- if (ppc_md.tce_flush)
- ppc_md.tce_flush(tbl);
+ if (tbl->it_ops->flush)
+ tbl->it_ops->flush(tbl);
/* Make sure updates are seen by hardware */
mb();
@@ -408,7 +408,7 @@ static void __iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
if (!iommu_free_check(tbl, dma_addr, npages))
return;
- ppc_md.tce_free(tbl, entry, npages);
+ tbl->it_ops->clear(tbl, entry, npages);
spin_lock_irqsave(&(pool->lock), flags);
bitmap_clear(tbl->it_map, free_entry, npages);
@@ -424,8 +424,8 @@ static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
* not do an mb() here on purpose, it is not needed on any of
* the current platforms.
*/
- if (ppc_md.tce_flush)
- ppc_md.tce_flush(tbl);
+ if (tbl->it_ops->flush)
+ tbl->it_ops->flush(tbl);
}
int iommu_map_sg(struct device *dev, struct iommu_table *tbl,
@@ -495,7 +495,7 @@ int iommu_map_sg(struct device *dev, struct iommu_table *tbl,
npages, entry, dma_addr);
/* Insert into HW table */
- build_fail = ppc_md.tce_build(tbl, entry, npages,
+ build_fail = tbl->it_ops->set(tbl, entry, npages,
vaddr & IOMMU_PAGE_MASK(tbl),
direction, attrs);
if(unlikely(build_fail))
@@ -534,8 +534,8 @@ int iommu_map_sg(struct device *dev, struct iommu_table *tbl,
}
/* Flush/invalidate TLB caches if necessary */
- if (ppc_md.tce_flush)
- ppc_md.tce_flush(tbl);
+ if (tbl->it_ops->flush)
+ tbl->it_ops->flush(tbl);
DBG("mapped %d elements:\n", outcount);
@@ -600,8 +600,8 @@ void iommu_unmap_sg(struct iommu_table *tbl, struct scatterlist *sglist,
* do not do an mb() here, the affected platforms do not need it
* when freeing.
*/
- if (ppc_md.tce_flush)
- ppc_md.tce_flush(tbl);
+ if (tbl->it_ops->flush)
+ tbl->it_ops->flush(tbl);
}
static void iommu_table_clear(struct iommu_table *tbl)
@@ -613,17 +613,17 @@ static void iommu_table_clear(struct iommu_table *tbl)
*/
if (!is_kdump_kernel() || is_fadump_active()) {
/* Clear the table in case firmware left allocations in it */
- ppc_md.tce_free(tbl, tbl->it_offset, tbl->it_size);
+ tbl->it_ops->clear(tbl, tbl->it_offset, tbl->it_size);
return;
}
#ifdef CONFIG_CRASH_DUMP
- if (ppc_md.tce_get) {
+ if (tbl->it_ops->get) {
unsigned long index, tceval, tcecount = 0;
/* Reserve the existing mappings left by the first kernel. */
for (index = 0; index < tbl->it_size; index++) {
- tceval = ppc_md.tce_get(tbl, index + tbl->it_offset);
+ tceval = tbl->it_ops->get(tbl, index + tbl->it_offset);
/*
* Freed TCE entry contains 0x7fffffffffffffff on JS20
*/
@@ -649,7 +649,8 @@ static void iommu_table_clear(struct iommu_table *tbl)
* Build a iommu_table structure. This contains a bit map which
* is used to manage allocation of the tce space.
*/
-struct iommu_table *iommu_init_table(struct iommu_table *tbl, int nid)
+struct iommu_table *iommu_init_table(struct iommu_table *tbl, int nid,
+ struct iommu_table_ops *ops)
{
unsigned long sz;
static int welcomed = 0;
@@ -657,6 +658,9 @@ struct iommu_table *iommu_init_table(struct iommu_table *tbl, int nid)
unsigned int i;
struct iommu_pool *p;
+ BUG_ON(!ops);
+ tbl->it_ops = ops;
+
/* number of bytes needed for the bitmap */
sz = BITS_TO_LONGS(tbl->it_size) * sizeof(unsigned long);
@@ -947,8 +951,8 @@ EXPORT_SYMBOL_GPL(iommu_tce_direction);
void iommu_flush_tce(struct iommu_table *tbl)
{
/* Flush/invalidate TLB caches if necessary */
- if (ppc_md.tce_flush)
- ppc_md.tce_flush(tbl);
+ if (tbl->it_ops->flush)
+ tbl->it_ops->flush(tbl);
/* Make sure updates are seen by hardware */
mb();
@@ -959,7 +963,7 @@ int iommu_tce_clear_param_check(struct iommu_table *tbl,
unsigned long ioba, unsigned long tce_value,
unsigned long npages)
{
- /* ppc_md.tce_free() does not support any value but 0 */
+ /* tbl->it_ops->clear() does not support any value but 0 */
if (tce_value)
return -EINVAL;
@@ -1007,9 +1011,9 @@ unsigned long iommu_clear_tce(struct iommu_table *tbl, unsigned long entry)
spin_lock(&(pool->lock));
- oldtce = ppc_md.tce_get(tbl, entry);
+ oldtce = tbl->it_ops->get(tbl, entry);
if (oldtce & (TCE_PCI_WRITE | TCE_PCI_READ))
- ppc_md.tce_free(tbl, entry, 1);
+ tbl->it_ops->clear(tbl, entry, 1);
else
oldtce = 0;
@@ -1056,10 +1060,10 @@ int iommu_tce_build(struct iommu_table *tbl, unsigned long entry,
spin_lock(&(pool->lock));
- oldtce = ppc_md.tce_get(tbl, entry);
+ oldtce = tbl->it_ops->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 = tbl->it_ops->set(tbl, entry, 1, hwaddr, direction, NULL);
spin_unlock(&(pool->lock));
diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c
index 904c661..66e4d74 100644
--- a/arch/powerpc/kernel/vio.c
+++ b/arch/powerpc/kernel/vio.c
@@ -1196,7 +1196,10 @@ static struct iommu_table *vio_build_iommu_table(struct vio_dev *dev)
tbl->it_type = TCE_VB;
tbl->it_blocksize = 16;
- return iommu_init_table(tbl, -1);
+ return iommu_init_table(tbl, -1,
+ firmware_has_feature(FW_FEATURE_LPAR) ?
+ &iommu_table_lpar_multi_ops :
+ &iommu_table_pseries_ops);
}
/**
diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c
index 2b90ff8..f6254f7 100644
--- a/arch/powerpc/platforms/cell/iommu.c
+++ b/arch/powerpc/platforms/cell/iommu.c
@@ -465,6 +465,11 @@ static inline u32 cell_iommu_get_ioid(struct device_node *np)
return *ioid;
}
+static struct iommu_table_ops cell_iommu_ops = {
+ .set = tce_build_cell,
+ .clear = tce_free_cell
+};
+
static struct iommu_window * __init
cell_iommu_setup_window(struct cbe_iommu *iommu, struct device_node *np,
unsigned long offset, unsigned long size,
@@ -492,7 +497,7 @@ cell_iommu_setup_window(struct cbe_iommu *iommu, struct device_node *np,
(offset >> window->table.it_page_shift) + pte_offset;
window->table.it_size = size >> window->table.it_page_shift;
- iommu_init_table(&window->table, iommu->nid);
+ iommu_init_table(&window->table, iommu->nid, &cell_iommu_ops);
pr_debug("\tioid %d\n", window->ioid);
pr_debug("\tblocksize %ld\n", window->table.it_blocksize);
@@ -1199,8 +1204,6 @@ static int __init cell_iommu_init(void)
/* Setup various ppc_md. callbacks */
ppc_md.pci_dma_dev_setup = cell_pci_dma_dev_setup;
ppc_md.dma_get_required_mask = cell_dma_get_required_mask;
- ppc_md.tce_build = tce_build_cell;
- ppc_md.tce_free = tce_free_cell;
if (!iommu_fixed_disabled && cell_iommu_fixed_mapping_init() == 0)
goto bail;
diff --git a/arch/powerpc/platforms/pasemi/iommu.c b/arch/powerpc/platforms/pasemi/iommu.c
index 2e576f2..eac33f4 100644
--- a/arch/powerpc/platforms/pasemi/iommu.c
+++ b/arch/powerpc/platforms/pasemi/iommu.c
@@ -132,6 +132,10 @@ static void iobmap_free(struct iommu_table *tbl, long index,
}
}
+static struct iommu_table_ops iommu_table_iobmap_ops = {
+ .set = iobmap_build,
+ .clear = iobmap_free
+};
static void iommu_table_iobmap_setup(void)
{
@@ -151,7 +155,7 @@ static void iommu_table_iobmap_setup(void)
* Should probably be 8 (64 bytes)
*/
iommu_table_iobmap.it_blocksize = 4;
- iommu_init_table(&iommu_table_iobmap, 0);
+ iommu_init_table(&iommu_table_iobmap, 0, &iommu_table_iobmap_ops);
pr_debug(" <- %s\n", __func__);
}
@@ -250,8 +254,6 @@ void __init iommu_init_early_pasemi(void)
ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_pasemi;
ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_pasemi;
- ppc_md.tce_build = iobmap_build;
- ppc_md.tce_free = iobmap_free;
set_pci_dma_ops(&dma_iommu_ops);
}
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
index f828c57..7482518 100644
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
@@ -691,7 +691,7 @@ static void pnv_pci_ioda_setup_dma_pe(struct pnv_phb *phb,
TCE_PCI_SWINV_FREE |
TCE_PCI_SWINV_PAIR);
}
- iommu_init_table(tbl, phb->hose->node);
+ iommu_init_table(tbl, phb->hose->node, &pnv_iommu_ops);
iommu_register_group(tbl, pe, &pnv_pci_ioda1_ops,
phb->hose->global_number, pe->pe_number);
@@ -831,7 +831,7 @@ static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb,
8);
tbl->it_type |= (TCE_PCI_SWINV_CREATE | TCE_PCI_SWINV_FREE);
}
- iommu_init_table(tbl, phb->hose->node);
+ iommu_init_table(tbl, phb->hose->node, &pnv_iommu_ops);
iommu_register_group(tbl, pe, &pnv_pci_ioda2_ops,
phb->hose->global_number, pe->pe_number);
diff --git a/arch/powerpc/platforms/powernv/pci-p5ioc2.c b/arch/powerpc/platforms/powernv/pci-p5ioc2.c
index b79066d..ea97414 100644
--- a/arch/powerpc/platforms/powernv/pci-p5ioc2.c
+++ b/arch/powerpc/platforms/powernv/pci-p5ioc2.c
@@ -87,7 +87,8 @@ static void pnv_pci_p5ioc2_dma_dev_setup(struct pnv_phb *phb,
struct pci_dev *pdev)
{
if (phb->p5ioc2.iommu_table.it_map == NULL) {
- iommu_init_table(&phb->p5ioc2.iommu_table, phb->hose->node);
+ iommu_init_table(&phb->p5ioc2.iommu_table, phb->hose->node,
+ &pnv_iommu_ops);
iommu_register_group(&phb->p5ioc2.iommu_table,
NULL, NULL,
pci_domain_nr(phb->hose->bus), phb->opal_id);
diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c
index cc54e3b..1179c63 100644
--- a/arch/powerpc/platforms/powernv/pci.c
+++ b/arch/powerpc/platforms/powernv/pci.c
@@ -628,18 +628,11 @@ static unsigned long pnv_tce_get(struct iommu_table *tbl, long index)
return ((u64 *)tbl->it_base)[index - tbl->it_offset];
}
-static int pnv_tce_build_rm(struct iommu_table *tbl, long index, long npages,
- unsigned long uaddr,
- enum dma_data_direction direction,
- struct dma_attrs *attrs)
-{
- return pnv_tce_build(tbl, index, npages, uaddr, direction, attrs, true);
-}
-
-static void pnv_tce_free_rm(struct iommu_table *tbl, long index, long npages)
-{
- pnv_tce_free(tbl, index, npages, true);
-}
+struct iommu_table_ops pnv_iommu_ops = {
+ .set = pnv_tce_build_vm,
+ .clear = pnv_tce_free_vm,
+ .get = pnv_tce_get,
+};
void pnv_pci_setup_iommu_table(struct iommu_table *tbl,
void *tce_mem, u64 tce_size,
@@ -673,7 +666,7 @@ static struct iommu_table *pnv_pci_setup_bml_iommu(struct pci_controller *hose)
return NULL;
pnv_pci_setup_iommu_table(tbl, __va(be64_to_cpup(basep)),
be32_to_cpup(sizep), 0, IOMMU_PAGE_SHIFT_4K);
- iommu_init_table(tbl, hose->node);
+ iommu_init_table(tbl, hose->node, &pnv_iommu_ops);
iommu_register_group(tbl, NULL, NULL, pci_domain_nr(hose->bus), 0);
/* Deal with SW invalidated TCEs when needed (BML way) */
@@ -816,11 +809,6 @@ void __init pnv_pci_init(void)
/* Configure IOMMU DMA hooks */
ppc_md.pci_dma_dev_setup = pnv_pci_dma_dev_setup;
- ppc_md.tce_build = pnv_tce_build_vm;
- ppc_md.tce_free = pnv_tce_free_vm;
- ppc_md.tce_build_rm = pnv_tce_build_rm;
- ppc_md.tce_free_rm = pnv_tce_free_rm;
- ppc_md.tce_get = pnv_tce_get;
ppc_md.pci_probe_mode = pnv_pci_probe_mode;
set_pci_dma_ops(&dma_iommu_ops);
diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h
index 32847a5..ab85743 100644
--- a/arch/powerpc/platforms/powernv/pci.h
+++ b/arch/powerpc/platforms/powernv/pci.h
@@ -200,6 +200,7 @@ extern struct pci_ops pnv_pci_ops;
#ifdef CONFIG_EEH
extern struct pnv_eeh_ops ioda_eeh_ops;
#endif
+extern struct iommu_table_ops pnv_iommu_ops;
void pnv_pci_dump_phb_diag_data(struct pci_controller *hose,
unsigned char *log_buff);
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index a047754..793f002 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -193,7 +193,7 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
int ret = 0;
unsigned long flags;
- if (npages == 1) {
+ if ((npages == 1) || !firmware_has_feature(FW_FEATURE_MULTITCE)) {
return tce_build_pSeriesLP(tbl, tcenum, npages, uaddr,
direction, attrs);
}
@@ -285,6 +285,9 @@ static void tce_freemulti_pSeriesLP(struct iommu_table *tbl, long tcenum, long n
{
u64 rc;
+ if (!firmware_has_feature(FW_FEATURE_MULTITCE))
+ return tce_free_pSeriesLP(tbl, tcenum, npages);
+
rc = plpar_tce_stuff((u64)tbl->it_index, (u64)tcenum << 12, 0, npages);
if (rc && printk_ratelimit()) {
@@ -460,7 +463,6 @@ static int tce_setrange_multi_pSeriesLP_walk(unsigned long start_pfn,
return tce_setrange_multi_pSeriesLP(start_pfn, num_pfn, arg);
}
-
#ifdef CONFIG_PCI
static void iommu_table_setparms(struct pci_controller *phb,
struct device_node *dn,
@@ -546,6 +548,12 @@ static void iommu_table_setparms_lpar(struct pci_controller *phb,
tbl->it_size = size >> tbl->it_page_shift;
}
+struct iommu_table_ops iommu_table_pseries_ops = {
+ .set = tce_build_pSeries,
+ .clear = tce_free_pSeries,
+ .get = tce_get_pseries
+};
+
static void pci_dma_bus_setup_pSeries(struct pci_bus *bus)
{
struct device_node *dn;
@@ -615,7 +623,8 @@ static void pci_dma_bus_setup_pSeries(struct pci_bus *bus)
pci->phb->node);
iommu_table_setparms(pci->phb, dn, tbl);
- pci->iommu_table = iommu_init_table(tbl, pci->phb->node);
+ pci->iommu_table = iommu_init_table(tbl, pci->phb->node,
+ &iommu_table_pseries_ops);
iommu_register_group(tbl, NULL, NULL, pci_domain_nr(bus), 0);
/* Divide the rest (1.75GB) among the children */
@@ -626,6 +635,11 @@ static void pci_dma_bus_setup_pSeries(struct pci_bus *bus)
pr_debug("ISA/IDE, window size is 0x%llx\n", pci->phb->dma_window_size);
}
+struct iommu_table_ops iommu_table_lpar_multi_ops = {
+ .set = tce_buildmulti_pSeriesLP,
+ .clear = tce_freemulti_pSeriesLP,
+ .get = tce_get_pSeriesLP
+};
static void pci_dma_bus_setup_pSeriesLP(struct pci_bus *bus)
{
@@ -660,7 +674,8 @@ static void pci_dma_bus_setup_pSeriesLP(struct pci_bus *bus)
tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL,
ppci->phb->node);
iommu_table_setparms_lpar(ppci->phb, pdn, tbl, dma_window);
- ppci->iommu_table = iommu_init_table(tbl, ppci->phb->node);
+ ppci->iommu_table = iommu_init_table(tbl, ppci->phb->node,
+ &iommu_table_lpar_multi_ops);
iommu_register_group(tbl, NULL, NULL, pci_domain_nr(bus), 0);
pr_debug(" created table: %p\n", ppci->iommu_table);
}
@@ -687,7 +702,8 @@ static void pci_dma_dev_setup_pSeries(struct pci_dev *dev)
tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL,
phb->node);
iommu_table_setparms(phb, dn, tbl);
- PCI_DN(dn)->iommu_table = iommu_init_table(tbl, phb->node);
+ PCI_DN(dn)->iommu_table = iommu_init_table(tbl, phb->node,
+ &iommu_table_pseries_ops);
iommu_register_group(tbl, NULL, NULL,
pci_domain_nr(phb->bus), 0);
set_iommu_table_base_and_group(&dev->dev,
@@ -1104,7 +1120,8 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev)
tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL,
pci->phb->node);
iommu_table_setparms_lpar(pci->phb, pdn, tbl, dma_window);
- pci->iommu_table = iommu_init_table(tbl, pci->phb->node);
+ pci->iommu_table = iommu_init_table(tbl, pci->phb->node,
+ &iommu_table_lpar_multi_ops);
iommu_register_group(tbl, NULL, NULL,
pci_domain_nr(pci->phb->bus), 0);
pr_debug(" created table: %p\n", pci->iommu_table);
@@ -1289,22 +1306,11 @@ void iommu_init_early_pSeries(void)
return;
if (firmware_has_feature(FW_FEATURE_LPAR)) {
- if (firmware_has_feature(FW_FEATURE_MULTITCE)) {
- ppc_md.tce_build = tce_buildmulti_pSeriesLP;
- ppc_md.tce_free = tce_freemulti_pSeriesLP;
- } else {
- ppc_md.tce_build = tce_build_pSeriesLP;
- ppc_md.tce_free = tce_free_pSeriesLP;
- }
- ppc_md.tce_get = tce_get_pSeriesLP;
ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_pSeriesLP;
ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_pSeriesLP;
ppc_md.dma_set_mask = dma_set_mask_pSeriesLP;
ppc_md.dma_get_required_mask = dma_get_required_mask_pSeriesLP;
} else {
- ppc_md.tce_build = tce_build_pSeries;
- ppc_md.tce_free = tce_free_pSeries;
- ppc_md.tce_get = tce_get_pseries;
ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_pSeries;
ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_pSeries;
}
@@ -1322,8 +1328,6 @@ static int __init disable_multitce(char *str)
firmware_has_feature(FW_FEATURE_LPAR) &&
firmware_has_feature(FW_FEATURE_MULTITCE)) {
printk(KERN_INFO "Disabling MULTITCE firmware feature\n");
- ppc_md.tce_build = tce_build_pSeriesLP;
- ppc_md.tce_free = tce_free_pSeriesLP;
powerpc_firmware_features &= ~FW_FEATURE_MULTITCE;
}
return 1;
diff --git a/arch/powerpc/sysdev/dart_iommu.c b/arch/powerpc/sysdev/dart_iommu.c
index 9e5353f..27721f5 100644
--- a/arch/powerpc/sysdev/dart_iommu.c
+++ b/arch/powerpc/sysdev/dart_iommu.c
@@ -286,6 +286,12 @@ static int __init dart_init(struct device_node *dart_node)
return 0;
}
+static struct iommu_table_ops iommu_dart_ops = {
+ .set = dart_build,
+ .clear = dart_free,
+ .flush = dart_flush,
+};
+
static void iommu_table_dart_setup(void)
{
iommu_table_dart.it_busno = 0;
@@ -298,7 +304,7 @@ static void iommu_table_dart_setup(void)
iommu_table_dart.it_base = (unsigned long)dart_vbase;
iommu_table_dart.it_index = 0;
iommu_table_dart.it_blocksize = 1;
- iommu_init_table(&iommu_table_dart, -1);
+ iommu_init_table(&iommu_table_dart, -1, &iommu_dart_ops);
/* Reserve the last page of the DART to avoid possible prefetch
* past the DART mapped area
@@ -386,11 +392,6 @@ void __init iommu_init_early_dart(void)
if (dart_init(dn) != 0)
goto bail;
- /* Setup low level TCE operations for the core IOMMU code */
- ppc_md.tce_build = dart_build;
- ppc_md.tce_free = dart_free;
- ppc_md.tce_flush = dart_flush;
-
/* Setup bypass if supported */
if (dart_is_u4)
ppc_md.dma_set_mask = dart_dma_set_mask;
--
2.0.0
^ permalink raw reply related
* [PATCH v4 12/16] powerpc/pseries/lpar: Enable VFIO
From: Alexey Kardashevskiy @ 2014-07-30 9:31 UTC (permalink / raw)
To: linuxppc-dev
Cc: Alexey Kardashevskiy, Michael Ellerman, Paul Mackerras,
Gavin Shan
In-Reply-To: <1406712695-9491-1-git-send-email-aik@ozlabs.ru>
The previous patch introduced iommu_table_ops::set_and_get() callback
which effectively disabled VFIO on pseries. This implements set_and_get()
for pseries/lpar so VFIO can work under pHyp again.
Since set_and_get() callback must return old TCE, it has to do H_GET_TCE
for every TCE being replaced, therefore VFIO's performance under pHyp
is expected to be slow.
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
arch/powerpc/platforms/pseries/iommu.c | 25 +++++++++++++++++++++++--
1 file changed, 23 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index 793f002..d3cded1 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -138,13 +138,14 @@ 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)
{
u64 rc = 0;
u64 proto_tce, tce;
u64 rpn;
- int ret = 0;
+ int ret = 0, i = 0;
long tcenum_start = tcenum, npages_start = npages;
rpn = __pa(uaddr) >> TCE_SHIFT;
@@ -154,6 +155,9 @@ static int tce_build_pSeriesLP(struct iommu_table *tbl, long tcenum,
while (npages--) {
tce = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT;
+ if (old_tces)
+ plpar_tce_get((u64)tbl->it_index, (u64)tcenum << 12,
+ &old_tces[i++]);
rc = plpar_tce_put((u64)tbl->it_index, (u64)tcenum << 12, tce);
if (unlikely(rc == H_NOT_ENOUGH_RESOURCES)) {
@@ -179,8 +183,9 @@ static int tce_build_pSeriesLP(struct iommu_table *tbl, long tcenum,
static DEFINE_PER_CPU(__be64 *, tce_page);
-static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
+static int tce_set_and_get_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,6 +200,7 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
if ((npages == 1) || !firmware_has_feature(FW_FEATURE_MULTITCE)) {
return tce_build_pSeriesLP(tbl, tcenum, npages, uaddr,
+ old_tces,
direction, attrs);
}
@@ -211,6 +217,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,
+ old_tces,
direction, attrs);
}
__get_cpu_var(tce_page) = tcep;
@@ -232,6 +239,10 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
for (l = 0; l < limit; l++) {
tcep[l] = cpu_to_be64(proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT);
rpn++;
+ if (old_tces)
+ plpar_tce_get((u64)tbl->it_index,
+ (u64)(tcenum + l) << 12,
+ &old_tces[tcenum + l]);
}
rc = plpar_tce_put_indirect((u64)tbl->it_index,
@@ -262,6 +273,15 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
return ret;
}
+static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
+ long npages, unsigned long uaddr,
+ enum dma_data_direction direction,
+ struct dma_attrs *attrs)
+{
+ return tce_set_and_get_pSeriesLP(tbl, tcenum, npages, uaddr, NULL,
+ direction, attrs);
+}
+
static void tce_free_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages)
{
u64 rc;
@@ -637,6 +657,7 @@ static void pci_dma_bus_setup_pSeries(struct pci_bus *bus)
struct iommu_table_ops iommu_table_lpar_multi_ops = {
.set = tce_buildmulti_pSeriesLP,
+ .set_and_get = tce_set_and_get_pSeriesLP,
.clear = tce_freemulti_pSeriesLP,
.get = tce_get_pSeriesLP
};
--
2.0.0
^ permalink raw reply related
* [PATCH v4 11/16] powerpc/powernv: Release replaced TCE
From: Alexey Kardashevskiy @ 2014-07-30 9:31 UTC (permalink / raw)
To: linuxppc-dev
Cc: Alexey Kardashevskiy, Michael Ellerman, Paul Mackerras,
Gavin Shan
In-Reply-To: <1406712695-9491-1-git-send-email-aik@ozlabs.ru>
At the moment writing new TCE value to the IOMMU table fails with EBUSY
if there is a valid entry already. However PAPR specification allows
the guest to write new TCE value without clearing it first.
This adds a set_and_get() callback to iommu_table_ops which does the same
thing as set() plus it returns replaced TCE(s) so the caller can release
the pages afterwards.
This makes iommu_tce_build() put pages returned by set_and_get().
Since now we depend on permission bits in TCE entries, this preserves
those bits in TCE in iommu_put_tce_user_mode().
This removes use of pool locks as those locks serve for TCE allocations
rathen than IOMMU table access and new set_and_get() callback provides
lockless way of safe pages release.
This disables external IOMMU use (i.e. VFIO) for IOMMUs which do not
implement set_and_get() callback. Therefore the "powernv" platform is
the only supported one.
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
Changes:
v4:
* this is merge+rework of
powerpc/powernv: Return non-zero TCE from pnv_tce_build
powerpc/iommu: Implement put_page() if TCE had non-zero value
powerpc/iommu: Extend ppc_md.tce_build(_rm) to return old TCE values
---
arch/powerpc/include/asm/iommu.h | 6 ++++++
arch/powerpc/kernel/iommu.c | 28 +++++++++++++++-------------
arch/powerpc/platforms/powernv/pci.c | 29 +++++++++++++++++++++++------
3 files changed, 44 insertions(+), 19 deletions(-)
diff --git a/arch/powerpc/include/asm/iommu.h b/arch/powerpc/include/asm/iommu.h
index c725e4a..4b13e4e 100644
--- a/arch/powerpc/include/asm/iommu.h
+++ b/arch/powerpc/include/asm/iommu.h
@@ -49,6 +49,12 @@ struct iommu_table_ops {
unsigned long uaddr,
enum dma_data_direction direction,
struct dma_attrs *attrs);
+ int (*set_and_get)(struct iommu_table *tbl,
+ long index, long npages,
+ unsigned long uaddr,
+ unsigned long *old_tces,
+ enum dma_data_direction direction,
+ struct dma_attrs *attrs);
void (*clear)(struct iommu_table *tbl,
long index, long npages);
unsigned long (*get)(struct iommu_table *tbl, long index);
diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
index 6a86788..ad52e00 100644
--- a/arch/powerpc/kernel/iommu.c
+++ b/arch/powerpc/kernel/iommu.c
@@ -1007,9 +1007,6 @@ EXPORT_SYMBOL_GPL(iommu_tce_put_param_check);
unsigned long iommu_clear_tce(struct iommu_table *tbl, unsigned long entry)
{
unsigned long oldtce;
- struct iommu_pool *pool = get_pool(tbl, entry);
-
- spin_lock(&(pool->lock));
oldtce = tbl->it_ops->get(tbl, entry);
if (oldtce & (TCE_PCI_WRITE | TCE_PCI_READ))
@@ -1017,8 +1014,6 @@ unsigned long iommu_clear_tce(struct iommu_table *tbl, unsigned long entry)
else
oldtce = 0;
- spin_unlock(&(pool->lock));
-
return oldtce;
}
EXPORT_SYMBOL_GPL(iommu_clear_tce);
@@ -1056,16 +1051,12 @@ int iommu_tce_build(struct iommu_table *tbl, unsigned long entry,
{
int ret = -EBUSY;
unsigned long oldtce;
- struct iommu_pool *pool = get_pool(tbl, entry);
- spin_lock(&(pool->lock));
+ ret = tbl->it_ops->set_and_get(tbl, entry, 1, hwaddr, &oldtce,
+ direction, NULL);
- oldtce = tbl->it_ops->get(tbl, entry);
- /* Add new entry if it is not busy */
- if (!(oldtce & (TCE_PCI_WRITE | TCE_PCI_READ)))
- ret = tbl->it_ops->set(tbl, entry, 1, hwaddr, direction, NULL);
-
- spin_unlock(&(pool->lock));
+ if (oldtce & (TCE_PCI_WRITE | TCE_PCI_READ))
+ put_page(pfn_to_page(__pa(oldtce) >> PAGE_SHIFT));
/* if (unlikely(ret))
pr_err("iommu_tce: %s failed on hwaddr=%lx ioba=%lx kva=%lx ret=%d\n",
@@ -1092,6 +1083,7 @@ int iommu_put_tce_user_mode(struct iommu_table *tbl, unsigned long entry,
return -EFAULT;
}
hwaddr = (unsigned long) page_address(page) + offset;
+ hwaddr |= tce & (TCE_PCI_READ | TCE_PCI_WRITE);
ret = iommu_tce_build(tbl, entry, hwaddr, direction);
if (ret)
@@ -1110,6 +1102,16 @@ int iommu_take_ownership(struct iommu_table *tbl)
unsigned long flags, i, sz = (tbl->it_size + 7) >> 3;
int ret = 0, bit0 = 0;
+ /*
+ * VFIO does not control TCE entries allocation and the guest
+ * can write new TCEs on top of existing ones so iommu_tce_build()
+ * must be able to release old pages. This functionality
+ * requires set_and_get() callback defined so if it is not
+ * implemented, we disallow taking ownership over the table.
+ */
+ if (!tbl->it_ops->set_and_get)
+ return -EINVAL;
+
spin_lock_irqsave(&tbl->large_pool.lock, flags);
for (i = 0; i < tbl->nr_pools; i++)
spin_lock(&tbl->pools[i].lock);
diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c
index 1179c63..629d443 100644
--- a/arch/powerpc/platforms/powernv/pci.c
+++ b/arch/powerpc/platforms/powernv/pci.c
@@ -572,12 +572,14 @@ 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;
__be64 *tcep, *tces;
u64 rpn;
+ long i;
proto_tce = TCE_PCI_READ; // Read allowed
@@ -587,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);
@@ -601,8 +607,18 @@ static int pnv_tce_build_vm(struct iommu_table *tbl, long index, long npages,
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, NULL, direction,
+ attrs, false);
+}
+
+static int pnv_tce_set_and_get_vm(struct iommu_table *tbl, long index,
+ long npages,
+ unsigned long uaddr, unsigned long *old_tces,
+ enum dma_data_direction direction,
+ struct dma_attrs *attrs)
+{
+ 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,6 +646,7 @@ static unsigned long pnv_tce_get(struct iommu_table *tbl, long index)
struct iommu_table_ops pnv_iommu_ops = {
.set = pnv_tce_build_vm,
+ .set_and_get = pnv_tce_set_and_get_vm,
.clear = pnv_tce_free_vm,
.get = pnv_tce_get,
};
--
2.0.0
^ permalink raw reply related
* [PATCH v4 14/16] vfio: powerpc/spapr: Reuse locked_vm accounting helpers
From: Alexey Kardashevskiy @ 2014-07-30 9:31 UTC (permalink / raw)
To: linuxppc-dev
Cc: Alexey Kardashevskiy, Michael Ellerman, Paul Mackerras,
Gavin Shan
In-Reply-To: <1406712695-9491-1-git-send-email-aik@ozlabs.ru>
There are helpers to account locked pages in locked_vm counter, this
reuses these helpers in VFIO-SPAPR-IOMMU driver.
While we are here, update the comment explaining why RLIMIT_MEMLOCK
might be required to be bigger than entire guest RAM.
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
Changes:
v4:
* added comment explaining how big the ulimit should be
* used try_increment_locked_vm/decrement_locked_vm
---
drivers/vfio/vfio_iommu_spapr_tce.c | 33 +++++++++++++++------------------
1 file changed, 15 insertions(+), 18 deletions(-)
diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
index d9845af..6ed0fc3 100644
--- a/drivers/vfio/vfio_iommu_spapr_tce.c
+++ b/drivers/vfio/vfio_iommu_spapr_tce.c
@@ -58,7 +58,6 @@ static void tce_iommu_take_ownership_notify(struct spapr_tce_iommu_group *data,
static int tce_iommu_enable(struct tce_container *container)
{
int ret = 0;
- unsigned long locked, lock_limit, npages;
struct iommu_table *tbl;
struct spapr_tce_iommu_group *data;
@@ -92,24 +91,24 @@ static int tce_iommu_enable(struct tce_container *container)
* Also we don't have a nice way to fail on H_PUT_TCE due to ulimits,
* that would effectively kill the guest at random points, much better
* enforcing the limit based on the max that the guest can map.
+ *
+ * Unfortunately at the moment it counts whole tables, no matter how
+ * much memory the guest has. I.e. for 4GB guest and 4 IOMMU groups
+ * each with 2GB DMA window, 8GB will be counted here. The reason for
+ * this is that we cannot tell here the amount of RAM used by the guest
+ * as this information is only available from KVM and VFIO is
+ * KVM agnostic.
*/
tbl = data->ops->get_table(data, TCE_DEFAULT_WINDOW);
if (!tbl)
return -ENXIO;
- down_write(¤t->mm->mmap_sem);
- npages = (tbl->it_size << IOMMU_PAGE_SHIFT_4K) >> PAGE_SHIFT;
- locked = current->mm->locked_vm + npages;
- lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
- if (locked > lock_limit && !capable(CAP_IPC_LOCK)) {
- pr_warn("RLIMIT_MEMLOCK (%ld) exceeded\n",
- rlimit(RLIMIT_MEMLOCK));
- ret = -ENOMEM;
- } else {
- current->mm->locked_vm += npages;
- container->enabled = true;
- }
- up_write(¤t->mm->mmap_sem);
+ ret = try_increment_locked_vm((tbl->it_size << IOMMU_PAGE_SHIFT_4K) >>
+ PAGE_SHIFT);
+ if (ret)
+ return ret;
+
+ container->enabled = true;
return ret;
}
@@ -135,10 +134,8 @@ static void tce_iommu_disable(struct tce_container *container)
if (!tbl)
return;
- down_write(¤t->mm->mmap_sem);
- current->mm->locked_vm -= (tbl->it_size <<
- IOMMU_PAGE_SHIFT_4K) >> PAGE_SHIFT;
- up_write(¤t->mm->mmap_sem);
+ decrement_locked_vm((tbl->it_size << IOMMU_PAGE_SHIFT_4K) >>
+ PAGE_SHIFT);
}
static void *tce_iommu_open(unsigned long arg)
--
2.0.0
^ permalink raw reply related
* [PATCH v4 15/16] vfio: powerpc/spapr: Use it_page_size
From: Alexey Kardashevskiy @ 2014-07-30 9:31 UTC (permalink / raw)
To: linuxppc-dev
Cc: Alexey Kardashevskiy, Michael Ellerman, Paul Mackerras,
Gavin Shan
In-Reply-To: <1406712695-9491-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 | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
index 6ed0fc3..48b256c 100644
--- a/drivers/vfio/vfio_iommu_spapr_tce.c
+++ b/drivers/vfio/vfio_iommu_spapr_tce.c
@@ -103,7 +103,7 @@ static int tce_iommu_enable(struct tce_container *container)
if (!tbl)
return -ENXIO;
- ret = try_increment_locked_vm((tbl->it_size << IOMMU_PAGE_SHIFT_4K) >>
+ ret = try_increment_locked_vm((tbl->it_size << tbl->it_page_shift) >>
PAGE_SHIFT);
if (ret)
return ret;
@@ -134,7 +134,7 @@ static void tce_iommu_disable(struct tce_container *container)
if (!tbl)
return;
- decrement_locked_vm((tbl->it_size << IOMMU_PAGE_SHIFT_4K) >>
+ decrement_locked_vm((tbl->it_size << tbl->it_page_shift) >>
PAGE_SHIFT);
}
@@ -207,8 +207,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))
@@ -261,17 +261,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);
@@ -312,13 +312,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 v4 13/16] powerpc/powernv: Implement Dynamic DMA windows (DDW) for IODA
From: Alexey Kardashevskiy @ 2014-07-30 9:31 UTC (permalink / raw)
To: linuxppc-dev
Cc: Alexey Kardashevskiy, Michael Ellerman, Paul Mackerras,
Gavin Shan
In-Reply-To: <1406712695-9491-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 | 158 +++++++++++++++++++++++++++++-
arch/powerpc/platforms/powernv/pci.h | 2 +
3 files changed, 180 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 7482518..6a847b2 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,147 @@ 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 */
+ tbl64->it_ops = pe->tce32.table.it_ops;
+
+ *ptbl = tbl64;
+
+ 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 ab85743..f25f633 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 v4 16/16] vfio: powerpc/spapr: Enable Dynamic DMA windows
From: Alexey Kardashevskiy @ 2014-07-30 9:31 UTC (permalink / raw)
To: linuxppc-dev
Cc: Alexey Kardashevskiy, Michael Ellerman, Paul Mackerras,
Gavin Shan
In-Reply-To: <1406712695-9491-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 | 173 +++++++++++++++++++++++++++++-
include/uapi/linux/vfio.h | 37 ++++++-
3 files changed, 209 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
index 6a847b2..f51afe2 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 48b256c..32e2804 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;
};
@@ -123,19 +124,36 @@ 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;
decrement_locked_vm((tbl->it_size << tbl->it_page_shift) >>
PAGE_SHIFT);
+
+ if (!container->start64)
+ return;
+
+ tbl = data->ops->get_table(data, container->start64);
+ if (!tbl)
+ return;
+
+ decrement_locked_vm((tbl->it_size << tbl->it_page_shift) >>
+ PAGE_SHIFT);
}
static void *tce_iommu_open(unsigned long arg)
@@ -210,6 +228,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;
@@ -335,6 +355,157 @@ 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 = try_increment_locked_vm((tbl->it_size <<
+ tbl->it_page_shift) >> PAGE_SHIFT);
+ 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);
+ decrement_locked_vm((tbl->it_size <<
+ tbl->it_page_shift) >> PAGE_SHIFT);
+ 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;
+
+ decrement_locked_vm((tbl->it_size << tbl->it_page_shift)
+ >> PAGE_SHIFT);
+ 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);
+
+ decrement_locked_vm((tbl->it_size << tbl->it_page_shift)
+ >> PAGE_SHIFT);
+ }
+
+ 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 v4 01/16] rcu: Define notrace version of list_for_each_entry_rcu and list_entry_rcu
From: Alexey Kardashevskiy @ 2014-07-30 9:31 UTC (permalink / raw)
To: linuxppc-dev
Cc: Alexey Kardashevskiy, Michael Ellerman, Paul Mackerras,
Gavin Shan
In-Reply-To: <1406712695-9491-1-git-send-email-aik@ozlabs.ru>
This defines list_for_each_entry_rcu_notrace which uses
new list_entry_rcu_notrace which uses rcu_dereference_raw_notrace instead
of rcu_dereference_raw whici allows us using
list_for_each_entry_rcu_notrace when MMU is off (real mode).
Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
include/linux/rculist.h | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/include/linux/rculist.h b/include/linux/rculist.h
index 8183b46..a155774 100644
--- a/include/linux/rculist.h
+++ b/include/linux/rculist.h
@@ -253,6 +253,25 @@ static inline void list_splice_init_rcu(struct list_head *list,
})
/**
+ * list_entry_rcu_notrace - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ *
+ * This primitive may safely run concurrently with the _rcu list-mutation
+ * primitives such as list_add_rcu() as long as it's guarded by rcu_read_lock().
+ *
+ * This is the same as list_entry_rcu() except that it does
+ * not do any RCU debugging or tracing.
+ */
+#define list_entry_rcu_notrace(ptr, type, member) \
+({ \
+ typeof(*ptr) __rcu *__ptr = (typeof(*ptr) __rcu __force *)ptr; \
+ container_of((typeof(ptr))rcu_dereference_raw_notrace(__ptr), \
+ type, member); \
+})
+
+/**
* Where are list_empty_rcu() and list_first_entry_rcu()?
*
* Implementing those functions following their counterparts list_empty() and
@@ -308,6 +327,25 @@ static inline void list_splice_init_rcu(struct list_head *list,
pos = list_entry_rcu(pos->member.next, typeof(*pos), member))
/**
+ * list_for_each_entry_rcu_notrace - iterate over rcu list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ *
+ * This is the same as list_for_each_entry_rcu() except that it does
+ * not do any RCU debugging or tracing.
+ */
+#define list_for_each_entry_rcu_notrace(pos, head, member) \
+ for (pos = list_entry_rcu_notrace((head)->next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry_rcu_notrace(pos->member.next, typeof(*pos), \
+ member))
+
+/**
* list_for_each_entry_continue_rcu - continue iteration over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
--
2.0.0
^ permalink raw reply related
* Re: [PATCH v4 16/16] vfio: powerpc/spapr: Enable Dynamic DMA windows
From: Alexey Kardashevskiy @ 2014-07-30 9:36 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Michael Ellerman, Paul Mackerras, Gavin Shan
In-Reply-To: <1406712695-9491-17-git-send-email-aik@ozlabs.ru>
On 07/30/2014 07:31 PM, Alexey Kardashevskiy wrote:
> 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 | 173 +++++++++++++++++++++++++++++-
> include/uapi/linux/vfio.h | 37 ++++++-
> 3 files changed, 209 insertions(+), 2 deletions(-)
>
> diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
> index 6a847b2..f51afe2 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;
Just noticed. This does not belong here, this must be moved to earlier patch.
> 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 48b256c..32e2804 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;
> };
>
>
> @@ -123,19 +124,36 @@ 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;
>
> decrement_locked_vm((tbl->it_size << tbl->it_page_shift) >>
> PAGE_SHIFT);
> +
> + if (!container->start64)
> + return;
> +
> + tbl = data->ops->get_table(data, container->start64);
> + if (!tbl)
> + return;
> +
> + decrement_locked_vm((tbl->it_size << tbl->it_page_shift) >>
> + PAGE_SHIFT);
> }
>
> static void *tce_iommu_open(unsigned long arg)
> @@ -210,6 +228,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;
> @@ -335,6 +355,157 @@ 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 = try_increment_locked_vm((tbl->it_size <<
> + tbl->it_page_shift) >> PAGE_SHIFT);
> + 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);
> + decrement_locked_vm((tbl->it_size <<
> + tbl->it_page_shift) >> PAGE_SHIFT);
> + 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;
> +
> + decrement_locked_vm((tbl->it_size << tbl->it_page_shift)
> + >> PAGE_SHIFT);
> + 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);
> +
> + decrement_locked_vm((tbl->it_size << tbl->it_page_shift)
> + >> PAGE_SHIFT);
> + }
> +
> + 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 */
>
--
Alexey
^ permalink raw reply
* [PATCH RFC] ASoC: fsl: Add Freescale Generic ASoC Sound Card with ASRC support
From: Nicolin Chen @ 2014-07-30 11:27 UTC (permalink / raw)
To: broonie
Cc: fabio.estevam, devicetree, alsa-devel, shawn.guo, timur, tiwai,
b42378, b02247, linux-kernel, lgirdwood, Li.Xiubo, mpa, perex,
linuxppc-dev
The Freescale Generic ASoC Sound Card is a general ASoC DAI Link driver that
can be used, ideally, for all Freescale CPU DAI drivers and external CODECs.
The idea of this generic sound card is a bit like ASoC Simple Card. However,
for Freescale SoCs (especially those released in recent years), most of them
have ASRC (Documentation/devicetree/bindings/sound/fsl,asrc.txt) inside. And
this is a specific feature that might be painstakingly controlled and merged
into the Simple Card driver.
So having this driver will allow all Freescale SoC users to benefit from the
simplification to support a new card and the capability of wide sample rates
support through ASRC.
The driver is initially designed for sound card using I2S or PCM DAI formats.
However, it's also possible to merge those non-I2S/PCM type sound cards, such
as S/PDIF audio and HDMI audio, into this card as long as the merge will not
break the original function and as long as there is something redundant that
can be abstracted along with I2S type sound cards.
As an initial version, it only supports three cards that I can test:
imx-audio-cs42888, a new card that links ESAI with CS42888 CODEC
imx-audio-sgtl5000, just like the old imx-sgtl5000.c driver
imx-audio-wm8962, just like the old imx-wm8962.c driver
The driver is also compatible with the old Device Tree bindings of WM8962 and
SGTL5000. So we may consider to remove those two drivers after this driver is
totally enabled. (It needs to be added into defconfig)
Signed-off-by: Nicolin Chen <nicoleotsuka@gmail.com>
---
.../devicetree/bindings/sound/fsl-asoc-card.txt | 82 +++
sound/soc/fsl/Kconfig | 16 +
sound/soc/fsl/Makefile | 2 +
sound/soc/fsl/fsl-asoc-card.c | 573 +++++++++++++++++++++
4 files changed, 673 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
create mode 100644 sound/soc/fsl/fsl-asoc-card.c
diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
new file mode 100644
index 0000000..a96774c
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
@@ -0,0 +1,82 @@
+Freescale Generic ASoC Sound Card with ASRC support
+
+The Freescale Generic ASoC Sound Card can be used, ideally, for all Freescale
+SoCs connecting with external CODECs.
+
+The idea of this generic sound card is a bit like ASoC Simple Card. However,
+for Freescale SoCs (especially those released in recent years), most of them
+have ASRC (Documentation/devicetree/bindings/sound/fsl,asrc.txt) inside. And
+this is a specific feature that might be painstakingly controlled and merged
+into the Simple Card.
+
+So having this generic sound card allows all Freescale SoC users to benefit
+from the simplification of a new card support and the capability of the wide
+sample rates support through ASRC.
+
+Note: The card is initially designed for those sound cards who use I2S and
+ PCM DAI formats. However, it'll be also possible to support those non
+ I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as long
+ as the driver has been properly upgraded.
+
+
+The compatible list for this generic sound card currently:
+ "fsl,imx-audio-cs42888"
+
+ "fsl,imx-audio-wm8962"
+ (compatible with Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt)
+
+ "fsl,imx-audio-sgtl5000"
+ (compatible with Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt)
+
+Required properties:
+
+ - compatible : Contains one of entries in the compatible list.
+
+ - model : The user-visible name of this sound complex
+
+ - audio-cpu : The phandle of an CPU DAI controller
+
+ - audio-codec : The phandle of an audio codec
+
+ - audio-routing : A list of the connections between audio components.
+ Each entry is a pair of strings, the first being the
+ connection's sink, the second being the connection's
+ source. There're a few pre-designed board connectors:
+ * Line Out Jack
+ * Line In Jack
+ * Headphone Jack
+ * Mic Jack
+ * Ext Spk
+ * AMIC (stands for Analog Microphone Jack)
+ * DMIC (stands for Digital Microphone Jack)
+
+ Note: The "Mic Jack" and "AMIC" are redundant while
+ coexsiting in order to support the old bindings
+ of wm8962 and sgtl5000.
+
+Optional properties:
+
+ - audio-asrc : The phandle of ASRC. It can be absent if there's no
+ need to add ASRC support via DPCM.
+
+Example:
+sound-cs42888 {
+ compatible = "fsl,imx-audio-cs42888";
+ model = "cs42888-audio";
+ audio-cpu = <&esai>;
+ audio-asrc = <&asrc>;
+ audio-codec = <&cs42888>;
+ audio-routing =
+ "Line Out Jack", "AOUT1L",
+ "Line Out Jack", "AOUT1R",
+ "Line Out Jack", "AOUT2L",
+ "Line Out Jack", "AOUT2R",
+ "Line Out Jack", "AOUT3L",
+ "Line Out Jack", "AOUT3R",
+ "Line Out Jack", "AOUT4L",
+ "Line Out Jack", "AOUT4R",
+ "AIN1L", "Line In Jack",
+ "AIN1R", "Line In Jack",
+ "AIN2L", "Line In Jack",
+ "AIN2R", "Line In Jack";
+};
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 2fb8a43..b55bf36 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -58,6 +58,22 @@ config SND_SOC_FSL_ESAI
config SND_SOC_FSL_UTILS
tristate
+config SND_SOC_FSL_ASOC_CARD
+ tristate "Generic ASoC Sound Card with ASRC support"
+ depends on OF && I2C
+ select SND_SOC_IMX_PCM_DMA
+ select SND_SOC_FSL_ESAI
+ select SND_SOC_FSL_SAI
+ select SND_SOC_FSL_SSI
+ select SND_SOC_CS42XX8_I2C
+ select SND_SOC_SGTL5000
+ select SND_SOC_WM8962
+ help
+ ALSA SoC Audio support with ASRC feature for Freescale SoCs that have
+ ESAI/SAI/SSI and connect with external CODECs such as WM8962, CS42888
+ and SGTL5000.
+ Say Y if you want to add support for Freescale Generic ASoC Sound Card.
+
config SND_SOC_IMX_PCM_DMA
tristate
select SND_SOC_GENERIC_DMAENGINE_PCM
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 9ff5926..8f6d84e 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-asoc-card-objs := fsl-asoc-card.o
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
@@ -19,6 +20,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_ASOC_CARD) += snd-soc-fsl-asoc-card.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
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
new file mode 100644
index 0000000..cf3f1f4
--- /dev/null
+++ b/sound/soc/fsl/fsl-asoc-card.c
@@ -0,0 +1,573 @@
+/*
+ * Freescale Generic ASoC Sound Card driver with ASRC
+ *
+ * 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/i2c.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "fsl_esai.h"
+#include "fsl_sai.h"
+#include "imx-audmux.h"
+
+#include "../codecs/sgtl5000.h"
+#include "../codecs/wm8962.h"
+
+#define RX 0
+#define TX 1
+
+/* Default DAI format without Master and Slave flag */
+#define DAI_FMT_BASE (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF)
+
+/**
+ * CODEC private data
+ *
+ * @mclk_freq: Clock rate of MCLK
+ * @mclk_id: MCLK (or main clock) id for set_sysclk()
+ * @fll_id: FLL (or secordary clock) id for set_sysclk()
+ * @pll_id: PLL id for set_pll()
+ */
+struct codec_priv {
+ unsigned long mclk_freq;
+ u32 mclk_id;
+ u32 fll_id;
+ u32 pll_id;
+};
+
+/**
+ * CPU private data
+ *
+ * @sysclk_freq[2]: SYSCLK rates for set_sysclk()
+ * @sysclk_dir[2]: SYSCLK directions for set_sysclk()
+ * @sysclk_id[2]: SYSCLK ids for set_sysclk()
+ *
+ * Note: [1] for tx and [0] for rx
+ */
+struct cpu_priv {
+ unsigned long sysclk_freq[2];
+ u32 sysclk_dir[2];
+ u32 sysclk_id[2];
+};
+
+/**
+ * Freescale Generic ASOC card private data
+ *
+ * @dai_link[3]: DAI link structure including normal one and DPCM link
+ * @pdev: platform device pointer
+ * @codec_priv: CODEC private data
+ * @cpu_priv: CPU private data
+ * @card: ASoC card structure
+ * @sample_rate: Current sample rate
+ * @sample_format: Current sample format
+ * @asrc_rate: ASRC sample rate used by Back-Ends
+ * @asrc_format: ASRC sample format used by Back-Ends
+ * @dai_fmt: DAI format between CPU and CODEC
+ * @name: Card name
+ */
+
+struct fsl_asoc_card_priv {
+ struct snd_soc_dai_link dai_link[3];
+ struct platform_device *pdev;
+ struct codec_priv codec_priv;
+ struct cpu_priv cpu_priv;
+ struct snd_soc_card card;
+ u32 sample_rate;
+ u32 sample_format;
+ u32 asrc_rate;
+ u32 asrc_format;
+ u32 dai_fmt;
+ char name[32];
+};
+
+/**
+ * This dapm route map exsits for DPCM link only.
+ * The other routes shall go through Device Tree.
+ */
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"CPU-Playback", NULL, "ASRC-Playback"},
+ {"Playback", NULL, "CPU-Playback"},
+ {"ASRC-Capture", NULL, "CPU-Capture"},
+ {"CPU-Capture", NULL, "Capture"},
+};
+
+/* Add all possible widgets into here without being redundant */
+static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = {
+ SND_SOC_DAPM_LINE("Line Out Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In Jack", NULL),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_MIC("AMIC", NULL),
+ SND_SOC_DAPM_MIC("DMIC", NULL),
+};
+
+static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ struct cpu_priv *cpu_priv = &priv->cpu_priv;
+ struct device *dev = rtd->card->dev;
+ int ret;
+
+ priv->sample_rate = params_rate(params);
+ priv->sample_format = params_format(params);
+
+ if (priv->card.set_bias_level)
+ return 0;
+
+ /* Specific configurations of DAIs starts from here */
+ ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, cpu_priv->sysclk_id[tx],
+ cpu_priv->sysclk_freq[tx],
+ cpu_priv->sysclk_dir[tx]);
+ if (ret) {
+ dev_err(dev, "failed to set sysclk for cpu dai\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops fsl_asoc_card_ops = {
+ .hw_params = fsl_asoc_card_hw_params,
+};
+
+static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_interval *rate;
+ struct snd_mask *mask;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ rate->max = rate->min = priv->asrc_rate;
+
+ mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ snd_mask_none(mask);
+ snd_mask_set(mask, priv->asrc_format);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link fsl_asoc_card_dai[] = {
+ /* Default ASoC DAI Link*/
+ {
+ .name = "HiFi",
+ .stream_name = "HiFi",
+ .ops = &fsl_asoc_card_ops,
+ },
+ /* DPCM Link between Front-End and Back-End (Optional) */
+ {
+ .name = "HiFi-ASRC-FE",
+ .stream_name = "HiFi-ASRC-FE",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .dynamic = 1,
+ },
+ {
+ .name = "HiFi-ASRC-BE",
+ .stream_name = "HiFi-ASRC-BE",
+ .platform_name = "snd-soc-dummy",
+ .be_hw_params_fixup = be_hw_params_fixup,
+ .ops = &fsl_asoc_card_ops,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .no_pcm = 1,
+ },
+};
+
+static int fsl_asoc_card_set_bias_level(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm,
+ enum snd_soc_bias_level level)
+{
+ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ struct codec_priv *codec_priv = &priv->codec_priv;
+ struct device *dev = card->dev;
+ unsigned int pll_out;
+ int ret;
+
+ if (dapm->dev != codec_dai->dev)
+ return 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (dapm->bias_level != SND_SOC_BIAS_STANDBY)
+ break;
+
+ if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE)
+ pll_out = priv->sample_rate * 384;
+ else
+ pll_out = priv->sample_rate * 256;
+
+ ret = snd_soc_dai_set_pll(codec_dai, codec_priv->pll_id,
+ codec_priv->mclk_id,
+ codec_priv->mclk_freq, pll_out);
+ if (ret) {
+ dev_err(dev, "failed to start FLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->fll_id,
+ pll_out, SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(dev, "failed to set SYSCLK: %d\n", ret);
+ return ret;
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (dapm->bias_level != SND_SOC_BIAS_PREPARE)
+ break;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
+ codec_priv->mclk_freq,
+ SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(dev, "failed to switch away from FLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, codec_priv->pll_id, 0, 0, 0);
+ if (ret) {
+ dev_err(dev, "failed to stop FLL: %d\n", ret);
+ return ret;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int fsl_asoc_card_audmux_init(struct device_node *np,
+ struct fsl_asoc_card_priv *priv)
+{
+ struct device *dev = &priv->pdev->dev;
+ u32 int_ptcr = 0, ext_ptcr = 0;
+ int int_port, ext_port;
+ int ret;
+
+ ret = of_property_read_u32(np, "mux-int-port", &int_port);
+ if (ret) {
+ dev_err(dev, "mux-int-port missing or invalid\n");
+ return ret;
+ }
+ ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
+ if (ret) {
+ dev_err(dev, "mux-ext-port missing or invalid\n");
+ return ret;
+ }
+
+ /*
+ * The port numbering in the hardware manual starts at 1, while
+ * the AUDMUX API expects it starts at 0.
+ */
+ int_port--;
+ ext_port--;
+
+ /*
+ * Use asynchronous mode (6 wires) for all cases.
+ * If only 4 wires are needed, just set SSI into
+ * synchronous mode and enable 4 PADs in IOMUX.
+ */
+ switch (priv->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) |
+ IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) |
+ IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
+ IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
+ IMX_AUDMUX_V2_PTCR_RFSDIR |
+ IMX_AUDMUX_V2_PTCR_RCLKDIR |
+ IMX_AUDMUX_V2_PTCR_TFSDIR |
+ IMX_AUDMUX_V2_PTCR_TCLKDIR;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ int_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | ext_port) |
+ IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
+ IMX_AUDMUX_V2_PTCR_RCLKDIR |
+ IMX_AUDMUX_V2_PTCR_TCLKDIR;
+ ext_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) |
+ IMX_AUDMUX_V2_PTCR_TFSEL(int_port) |
+ IMX_AUDMUX_V2_PTCR_RFSDIR |
+ IMX_AUDMUX_V2_PTCR_TFSDIR;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ int_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | ext_port) |
+ IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
+ IMX_AUDMUX_V2_PTCR_RFSDIR |
+ IMX_AUDMUX_V2_PTCR_TFSDIR;
+ ext_ptcr = IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) |
+ IMX_AUDMUX_V2_PTCR_TCSEL(int_port) |
+ IMX_AUDMUX_V2_PTCR_RCLKDIR |
+ IMX_AUDMUX_V2_PTCR_TCLKDIR;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ ext_ptcr = IMX_AUDMUX_V2_PTCR_RFSEL(8 | int_port) |
+ IMX_AUDMUX_V2_PTCR_RCSEL(8 | int_port) |
+ IMX_AUDMUX_V2_PTCR_TFSEL(int_port) |
+ IMX_AUDMUX_V2_PTCR_TCSEL(int_port) |
+ IMX_AUDMUX_V2_PTCR_RFSDIR |
+ IMX_AUDMUX_V2_PTCR_RCLKDIR |
+ IMX_AUDMUX_V2_PTCR_TFSDIR |
+ IMX_AUDMUX_V2_PTCR_TCLKDIR;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Asynchronous mode can not be set along with RCLKDIR */
+ ret = imx_audmux_v2_configure_port(int_port, 0,
+ IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
+ if (ret) {
+ dev_err(dev, "audmux internal port setup failed\n");
+ return ret;
+ }
+
+ ret = imx_audmux_v2_configure_port(int_port, int_ptcr,
+ IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
+ if (ret) {
+ dev_err(dev, "audmux internal port setup failed\n");
+ return ret;
+ }
+
+ ret = imx_audmux_v2_configure_port(ext_port, 0,
+ IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
+ if (ret) {
+ dev_err(dev, "audmux external port setup failed\n");
+ return ret;
+ }
+
+ ret = imx_audmux_v2_configure_port(ext_port, ext_ptcr,
+ IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
+ if (ret) {
+ dev_err(dev, "audmux external port setup failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
+{
+ struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ struct codec_priv *codec_priv = &priv->codec_priv;
+ struct device *dev = card->dev;
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
+ codec_priv->mclk_freq, SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(dev, "failed to set sysclk in %s\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fsl_asoc_card_probe(struct platform_device *pdev)
+{
+ struct device_node *cpu_np, *codec_np, *asrc_np;
+ struct device_node *np = pdev->dev.of_node;
+ struct platform_device *asrc_pdev = NULL;
+ struct platform_device *cpu_pdev;
+ struct fsl_asoc_card_priv *priv;
+ struct i2c_client *codec_dev;
+ struct clk *codec_clk;
+ u32 width;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ cpu_np = of_parse_phandle(np, "audio-cpu", 0);
+ /* Give a chance to old DT binding */
+ if (!cpu_np)
+ cpu_np = of_parse_phandle(np, "ssi-controller", 0);
+ codec_np = of_parse_phandle(np, "audio-codec", 0);
+ if (!cpu_np || !codec_np) {
+ dev_err(&pdev->dev, "phandle missing or invalid\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ cpu_pdev = of_find_device_by_node(cpu_np);
+ if (!cpu_pdev) {
+ dev_err(&pdev->dev, "failed to find CPU DAI device\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ codec_dev = of_find_i2c_device_by_node(codec_np);
+ if (!codec_dev) {
+ dev_err(&pdev->dev, "failed to find codec platform device\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ asrc_np = of_parse_phandle(np, "audio-asrc", 0);
+ if (asrc_np)
+ asrc_pdev = of_find_device_by_node(asrc_np);
+
+ /* Get the MCLK rate only, and leave it controlled by CODEC drivers */
+ codec_clk = clk_get(&codec_dev->dev, NULL);
+ if (!IS_ERR(codec_clk)) {
+ priv->codec_priv.mclk_freq = clk_get_rate(codec_clk);
+ clk_put(codec_clk);
+ }
+
+ /* Default sample rate and format, will be updated in hw_params() */
+ priv->sample_rate = 44100;
+ priv->sample_format = SNDRV_PCM_FORMAT_S16_LE;
+
+ /* Assign a default DAI format, and allow each card to overwrite it */
+ priv->dai_fmt = DAI_FMT_BASE;
+
+ /* Diversify the card configurations */
+ if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) {
+ priv->card.set_bias_level = NULL;
+ priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv.mclk_freq;
+ priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq;
+ priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT;
+ priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) {
+ priv->codec_priv.mclk_id = SGTL5000_SYSCLK;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) {
+ priv->card.set_bias_level = fsl_asoc_card_set_bias_level;
+ priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK;
+ priv->codec_priv.fll_id = WM8962_SYSCLK_FLL;
+ priv->codec_priv.pll_id = WM8962_FLL;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ } else {
+ dev_err(&pdev->dev, "unknown Device Tree compatible\n");
+ return -EINVAL;
+ }
+
+ /* Common settings for corresponding Freescale CPU DAI driver */
+ if (strstr(cpu_np->name, "ssi")) {
+ /* Only SSI needs to configure AUDMUX */
+ ret = fsl_asoc_card_audmux_init(np, priv);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to init audmux\n");
+ goto fail;
+ }
+ } else if (strstr(cpu_np->name, "esai")) {
+ priv->cpu_priv.sysclk_id[1] = ESAI_HCKT_EXTAL;
+ priv->cpu_priv.sysclk_id[0] = ESAI_HCKR_EXTAL;
+ } else if (strstr(cpu_np->name, "sai")) {
+ priv->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1;
+ priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1;
+ }
+
+ sprintf(priv->name, "%s-audio", codec_dev->name);
+
+ /* Initialize sound card */
+ priv->pdev = pdev;
+ priv->card.dev = &pdev->dev;
+ priv->card.name = priv->name;
+ priv->card.dai_link = priv->dai_link;
+ priv->card.dapm_routes = audio_map;
+ priv->card.late_probe = fsl_asoc_card_late_probe;
+ priv->card.num_dapm_routes = ARRAY_SIZE(audio_map);
+ priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets;
+ priv->card.num_dapm_widgets = ARRAY_SIZE(fsl_asoc_card_dapm_widgets);
+
+ memcpy(priv->dai_link, fsl_asoc_card_dai,
+ sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link));
+
+ /* Normal DAI Link */
+ priv->dai_link[0].cpu_of_node = cpu_np;
+ priv->dai_link[0].codec_of_node = codec_np;
+ priv->dai_link[0].codec_dai_name = codec_dev->name;
+ priv->dai_link[0].platform_of_node = cpu_np;
+ priv->dai_link[0].dai_fmt = priv->dai_fmt;
+ priv->card.num_links = 1;
+
+ if (asrc_pdev) {
+ /* DPCM DAI Links only if ASRC exsits */
+ priv->dai_link[1].cpu_of_node = asrc_np;
+ priv->dai_link[1].platform_of_node = asrc_np;
+ priv->dai_link[2].codec_dai_name = codec_dev->name;
+ priv->dai_link[2].codec_of_node = codec_np;
+ priv->dai_link[2].cpu_of_node = cpu_np;
+ priv->dai_link[2].dai_fmt = priv->dai_fmt;
+ priv->card.num_links = 3;
+
+ ret = of_property_read_u32(asrc_np, "fsl,asrc-rate",
+ &priv->asrc_rate);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get output rate\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ ret = of_property_read_u32(asrc_np, "fsl,asrc-width", &width);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get output rate\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (width == 24)
+ priv->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
+ else
+ priv->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
+ }
+
+ /* Finish card registering */
+ platform_set_drvdata(pdev, priv);
+ snd_soc_card_set_drvdata(&priv->card, priv);
+
+ ret = devm_snd_soc_register_card(&pdev->dev, &priv->card);
+ if (ret)
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+
+fail:
+ of_node_put(codec_np);
+ of_node_put(asrc_np);
+ of_node_put(cpu_np);
+
+ return ret;
+}
+
+static const struct of_device_id fsl_asoc_card_dt_ids[] = {
+ { .compatible = "fsl,imx-audio-cs42888", },
+ { .compatible = "fsl,imx-audio-sgtl5000", },
+ { .compatible = "fsl,imx-audio-wm8962", },
+ {}
+};
+
+static struct platform_driver fsl_asoc_card_driver = {
+ .probe = fsl_asoc_card_probe,
+ .driver = {
+ .name = "fsl-asoc-card",
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = fsl_asoc_card_dt_ids,
+ },
+};
+module_platform_driver(fsl_asoc_card_driver);
+
+MODULE_DESCRIPTION("Freescale Generic ASoC Sound Card driver with ASRC");
+MODULE_AUTHOR("Nicolin Chen <nicoleotsuka@gmail.com>");
+MODULE_ALIAS("platform:fsl-asoc-card");
+MODULE_LICENSE("GPL");
--
1.8.4
^ permalink raw reply related
* [PATCH 3.12 71/94] locking/mutex: Disable optimistic spinning on some architectures
From: Jiri Slaby @ 2014-07-30 12:15 UTC (permalink / raw)
To: stable
Cc: Peter Zijlstra, Catalin Marinas, Will Deacon, James Bottomley,
Davidlohr Bueso, sparclinux, Jiri Slaby, Ingo Molnar,
Russell King, James E.J. Bottomley, Linus Torvalds, Paul McKenney,
James Hogan, Chris Metcalf, John David Anglin, linux-arm-kernel,
Jason Low, Waiman Long, Vineet Gupta, linux-kernel, linuxppc-dev,
David Miller
In-Reply-To: <cover.1406722269.git.jslaby@suse.cz>
From: Peter Zijlstra <peterz@infradead.org>
3.12-stable review patch. If anyone has any objections, please let me know.
===============
commit 4badad352a6bb202ec68afa7a574c0bb961e5ebc upstream.
The optimistic spin code assumes regular stores and cmpxchg() play nice;
this is found to not be true for at least: parisc, sparc32, tile32,
metag-lock1, arc-!llsc and hexagon.
There is further wreckage, but this in particular seemed easy to
trigger, so blacklist this.
Opt in for known good archs.
Signed-off-by: Peter Zijlstra <peterz@infradead.org>
Reported-by: Mikulas Patocka <mpatocka@redhat.com>
Cc: David Miller <davem@davemloft.net>
Cc: Chris Metcalf <cmetcalf@tilera.com>
Cc: James Bottomley <James.Bottomley@hansenpartnership.com>
Cc: Vineet Gupta <vgupta@synopsys.com>
Cc: Jason Low <jason.low2@hp.com>
Cc: Waiman Long <waiman.long@hp.com>
Cc: "James E.J. Bottomley" <jejb@parisc-linux.org>
Cc: Paul McKenney <paulmck@linux.vnet.ibm.com>
Cc: John David Anglin <dave.anglin@bell.net>
Cc: James Hogan <james.hogan@imgtec.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Davidlohr Bueso <davidlohr@hp.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Will Deacon <will.deacon@arm.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Cc: linuxppc-dev@lists.ozlabs.org
Cc: sparclinux@vger.kernel.org
Link: http://lkml.kernel.org/r/20140606175316.GV13930@laptop.programming.kicks-ass.net
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
---
arch/arm/Kconfig | 1 +
arch/arm64/Kconfig | 1 +
arch/powerpc/Kconfig | 1 +
arch/sparc/Kconfig | 1 +
arch/x86/Kconfig | 1 +
kernel/Kconfig.locks | 5 ++++-
6 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index e47fcd1e9645..99e1ce978cf9 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -5,6 +5,7 @@ config ARM
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
select ARCH_HAVE_CUSTOM_GPIO_H
+ select ARCH_SUPPORTS_ATOMIC_RMW
select ARCH_WANT_IPC_PARSE_VERSION
select BUILDTIME_EXTABLE_SORT if MMU
select CLONE_BACKWARDS
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index c04454876bcb..fe70eaea0e28 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1,6 +1,7 @@
config ARM64
def_bool y
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
+ select ARCH_SUPPORTS_ATOMIC_RMW
select ARCH_WANT_OPTIONAL_GPIOLIB
select ARCH_WANT_COMPAT_IPC_PARSE_VERSION
select ARCH_WANT_FRAME_POINTERS
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index d5d026b6d237..2e0ddfadc0b9 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -138,6 +138,7 @@ config PPC
select OLD_SIGSUSPEND
select OLD_SIGACTION if PPC32
select HAVE_DEBUG_STACKOVERFLOW
+ select ARCH_SUPPORTS_ATOMIC_RMW
config EARLY_PRINTK
bool
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
index 4e5683877b93..d60f34dbae89 100644
--- a/arch/sparc/Kconfig
+++ b/arch/sparc/Kconfig
@@ -75,6 +75,7 @@ config SPARC64
select ARCH_HAVE_NMI_SAFE_CMPXCHG
select HAVE_C_RECORDMCOUNT
select NO_BOOTMEM
+ select ARCH_SUPPORTS_ATOMIC_RMW
config ARCH_DEFCONFIG
string
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index eb2dfa61eabe..9dc1a24d41b8 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -123,6 +123,7 @@ config X86
select COMPAT_OLD_SIGACTION if IA32_EMULATION
select RTC_LIB
select HAVE_DEBUG_STACKOVERFLOW
+ select ARCH_SUPPORTS_ATOMIC_RMW
config INSTRUCTION_DECODER
def_bool y
diff --git a/kernel/Kconfig.locks b/kernel/Kconfig.locks
index d2b32ac27a39..ecee67a00f5f 100644
--- a/kernel/Kconfig.locks
+++ b/kernel/Kconfig.locks
@@ -220,6 +220,9 @@ config INLINE_WRITE_UNLOCK_IRQRESTORE
endif
+config ARCH_SUPPORTS_ATOMIC_RMW
+ bool
+
config MUTEX_SPIN_ON_OWNER
def_bool y
- depends on SMP && !DEBUG_MUTEXES
+ depends on SMP && !DEBUG_MUTEXES && ARCH_SUPPORTS_ATOMIC_RMW
--
2.0.1
^ permalink raw reply related
* Re: [PATCH 4/4] ASoC: fsl_ssi: Add stream names for DPCM usage
From: Timur Tabi @ 2014-07-30 12:19 UTC (permalink / raw)
To: Nicolin Chen, broonie
Cc: alsa-devel, tiwai, b42378, b02247, linux-kernel, lgirdwood,
Li.Xiubo, perex, linuxppc-dev
In-Reply-To: <891b9f4edbba81a03c2b8350abc5ccf923db9260.1406688049.git.nicoleotsuka@gmail.com>
Nicolin Chen wrote:
> DPCM needs extra dapm routes in the machine driver to route audio
> between Front-End and Back-End. In order to differ the stream names
> in the route map from CODECs, we here add specific stream names to
> SSI driver so that we can implement ASRC via DPCM to it.
>
> Signed-off-by: Nicolin Chen<nicoleotsuka@gmail.com>
Acked-by: Timur Tabi <timur@tabi.org>
^ permalink raw reply
* Re: [2/2] powerpc/fsl-booke: Add initial T1042RDB_PI board support
From: Scott Wood @ 2014-07-30 20:12 UTC (permalink / raw)
To: Priyanka Jain
Cc: devicetree, linuxppc-dev, Prabhakar Kushwaha, Poonam Aggrwal
In-Reply-To: <1404879851-2914-1-git-send-email-Priyanka.Jain@freescale.com>
On Wed, Jul 09, 2014 at 09:54:11AM +0530, Priyanka Jain wrote:
> diff --git a/arch/powerpc/boot/dts/t104xrdb.dtsi b/arch/powerpc/boot/dts/t104xrdb.dtsi
> index 9aaefa5..e7e765f 100644
> --- a/arch/powerpc/boot/dts/t104xrdb.dtsi
> +++ b/arch/powerpc/boot/dts/t104xrdb.dtsi
> @@ -57,7 +57,8 @@
> };
>
> cpld@3,0 {
> - compatible = "fsl,t1040rdb-cpld","fsl,t1042rdb-cpld";
> + compatible = "fsl,t1040rdb-cpld","fsl,t1042rdb-cpld",
> + "fsl,t1042rdb_pi-cpld";
> reg = <3 0 0x300>;
> };
> };
What's going on here? This file is used by all three boards. If you
need to distinguish one board's CPLD from another's, you'll have to do it
somewhere else. If the CPLDs are exactly the same and no distinction
needs to be made, then you don't need three compatible strings. Even
then, you may wish to specify the exact board as the first compatible
string, but again you'll need to patch that in elsewhere so that it
actually matches the board.
-Scott
^ permalink raw reply
* Re: [PATCH v2 5/7] powerpc/corenet: Add MDIO bus muxing support to the board device tree(s)
From: Emil Medve @ 2014-07-30 21:52 UTC (permalink / raw)
To: Scott Wood; +Cc: linuxppc-dev
In-Reply-To: <1406663895.29414.240.camel@snotra.buserror.net>
Hello Scott,
On 07/29/2014 02:58 PM, Scott Wood wrote:
> On Mon, 2014-07-28 at 06:51 +0000, Emil Medve wrote:
>> Hello Scott,
>>
>>
>> Scott Wood <scottwood <at> freescale.com> writes:
>>> On Wed, 2014-07-16 at 15:17 -0500, Shruti Kanetkar wrote:
>>>> + mdio <at> fd000 {
>>>> + /* For 10g interfaces */
>>>> + phy_xaui_slot1: xaui-phy <at> slot1 {
>>>> + status = "disabled";
>>>> + compatible = "ethernet-phy-ieee802.3-c45";
>>>> + reg = <0x7>; /* default switch setting on slot1 of AMC2PEX */
>>>> + };
>>>
>>> Why xaui-phy and not ethernet-phy?
>>>
>>> As for the device_type discussion from v1, there is a generic binding
>>> that says device_type "should" be ethernet-phy.
>>
>> I have no strong feelings about this and we can use ethernet-phy, but:
>>
>> 1. The binding is old/stale (?) as it still uses device_type and the kernel
>> doesn't seem to use anymore the device_type for PHY(s)
>
> Yes.
>
>> 2. The binding asks "ethernet-phy" for the device_type property, not for the
>> name. As such TBI PHY(s) use (upstream) the tbi-phy@ node name
>
> It shows ethernet-phy as the name in the example. ePAPR urges generic
> node names (this was also a recommendation for IEEE1275), and has
> ethernet-phy on the preferred list. Is a xaui-phy not an ethernet phy?
So you thinking somebody should cleanup all the sgmii-phy and tbi-phy
node names, huh?
It seems that a number of tbi-phy instances slipped by you:
1be62c6 powerpc/mpc85xx: Add BSC9132 QDS Support
bf57aeb powerpc/85xx: add the P1020RDB-PD DTS support
8a6be2b powerpc/85xx: Add TWR-P1025 board support
>>>> + mdio0: mdio <at> fc000 {
>>>> + };
>>>
>>> Why is the empty node needed?
>>
>> For the label
>
> For mdio-parent-bus, or is there some other dts layer that makes this
> node non-empty?
'powerpc/corenet: Create the dts components for the DPAA FMan' -
http://patchwork.ozlabs.org/patch/370872 and 'powerpc/corenet: Add DPAA
FMan support to the SoC device tree(s)' -
http://patchwork.ozlabs.org/patch/370868 add content to said node
Cheers,
^ permalink raw reply
* Re: [PATCH v2 5/7] powerpc/corenet: Add MDIO bus muxing support to the board device tree(s)
From: Scott Wood @ 2014-07-31 2:30 UTC (permalink / raw)
To: Emil Medve; +Cc: linuxppc-dev
In-Reply-To: <53D96906.1040407@Freescale.com>
On Wed, 2014-07-30 at 16:52 -0500, Emil Medve wrote:
> Hello Scott,
>
>
> On 07/29/2014 02:58 PM, Scott Wood wrote:
> > On Mon, 2014-07-28 at 06:51 +0000, Emil Medve wrote:
> >> Hello Scott,
> >>
> >>
> >> Scott Wood <scottwood <at> freescale.com> writes:
> >>> On Wed, 2014-07-16 at 15:17 -0500, Shruti Kanetkar wrote:
> >>>> + mdio <at> fd000 {
> >>>> + /* For 10g interfaces */
> >>>> + phy_xaui_slot1: xaui-phy <at> slot1 {
> >>>> + status = "disabled";
> >>>> + compatible = "ethernet-phy-ieee802.3-c45";
> >>>> + reg = <0x7>; /* default switch setting on slot1 of AMC2PEX */
> >>>> + };
> >>>
> >>> Why xaui-phy and not ethernet-phy?
> >>>
> >>> As for the device_type discussion from v1, there is a generic binding
> >>> that says device_type "should" be ethernet-phy.
> >>
> >> I have no strong feelings about this and we can use ethernet-phy, but:
> >>
> >> 1. The binding is old/stale (?) as it still uses device_type and the kernel
> >> doesn't seem to use anymore the device_type for PHY(s)
> >
> > Yes.
> >
> >> 2. The binding asks "ethernet-phy" for the device_type property, not for the
> >> name. As such TBI PHY(s) use (upstream) the tbi-phy@ node name
> >
> > It shows ethernet-phy as the name in the example. ePAPR urges generic
> > node names (this was also a recommendation for IEEE1275), and has
> > ethernet-phy on the preferred list. Is a xaui-phy not an ethernet phy?
>
> So you thinking somebody should cleanup all the sgmii-phy and tbi-phy
> node names, huh?
No, I was just wondering why we're adding yet another name, and whether
there's any value in it.
> It seems that a number of tbi-phy instances slipped by you:
>
> 1be62c6 powerpc/mpc85xx: Add BSC9132 QDS Support
> bf57aeb powerpc/85xx: add the P1020RDB-PD DTS support
> 8a6be2b powerpc/85xx: Add TWR-P1025 board support
tbi-phy is existing practice. xaui-phy isn't.
> >>>> + mdio0: mdio <at> fc000 {
> >>>> + };
> >>>
> >>> Why is the empty node needed?
> >>
> >> For the label
> >
> > For mdio-parent-bus, or is there some other dts layer that makes this
> > node non-empty?
>
> 'powerpc/corenet: Create the dts components for the DPAA FMan' -
> http://patchwork.ozlabs.org/patch/370872
Why does this patch define the mdio0 label for mdio@e1120, but not
define a label for any other node?
> and 'powerpc/corenet: Add DPAA
> FMan support to the SoC device tree(s)' -
> http://patchwork.ozlabs.org/patch/370868 add content to said node
This one adds content to some mdio nodes, none of which are mdio@fc000
or &mdio0.
-Scott
^ permalink raw reply
* [PATCH] ASoC: fsl_asrc: Fix sparse warnings in FSL_ASRC_FORMATS due to typo
From: Nicolin Chen @ 2014-07-31 4:07 UTC (permalink / raw)
To: broonie; +Cc: alsa-devel, b42378, linuxppc-dev, linux-kernel, timur
reproduce: make C=1 CF=-D__CHECK_ENDIAN__
sparse warnings: (new ones prefixed by >>)
>> sound/soc/fsl/fsl_asrc.c:563:28: sparse: restricted snd_pcm_format_t degrades to integer
>> sound/soc/fsl/fsl_asrc.c:570:28: sparse: restricted snd_pcm_format_t degrades to integer
vim +563 sound/soc/fsl/fsl_asrc.c
557 .probe = fsl_asrc_dai_probe,
558 .playback = {
559 .stream_name = "ASRC-Playback",
560 .channels_min = 1,
561 .channels_max = 10,
562 .rates = FSL_ASRC_RATES,
> 563 .formats = FSL_ASRC_FORMATS,
564 },
565 .capture = {
566 .stream_name = "ASRC-Capture",
567 .channels_min = 1,
568 .channels_max = 10,
569 .rates = FSL_ASRC_RATES,
> 570 .formats = FSL_ASRC_FORMATS,
571 },
572 .ops = &fsl_asrc_dai_ops,
573 };
Reported-by: kbuild test robot <fengguang.wu@intel.com>
Signed-off-by: Nicolin Chen <nicoleotsuka@gmail.com>
---
sound/soc/fsl/fsl_asrc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index 41699f7..cdb5779 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -551,7 +551,7 @@ static int fsl_asrc_dai_probe(struct snd_soc_dai *dai)
#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)
+ SNDRV_PCM_FMTBIT_S20_3LE)
static struct snd_soc_dai_driver fsl_asrc_dai = {
.probe = fsl_asrc_dai_probe,
--
1.8.4
^ permalink raw reply related
* RE: [2/2] powerpc/fsl-booke: Add initial T1042RDB_PI board support
From: Priyanka Jain @ 2014-07-31 4:37 UTC (permalink / raw)
To: Scott Wood
Cc: devicetree@vger.kernel.org, linuxppc-dev@lists.ozlabs.org,
prabhakar@freescale.com, Poonam.Aggrwal@freescale.com
In-Reply-To: <20140730201250.GA29789@home.buserror.net>
-----Original Message-----
From: Wood Scott-B07421=20
Sent: Thursday, July 31, 2014 1:43 AM
To: Jain Priyanka-B32167
Cc: devicetree@vger.kernel.org; linuxppc-dev@lists.ozlabs.org; Aggrwal Poon=
am-B10812; Kushwaha Prabhakar-B32579
Subject: Re: [2/2] powerpc/fsl-booke: Add initial T1042RDB_PI board support
On Wed, Jul 09, 2014 at 09:54:11AM +0530, Priyanka Jain wrote:
> diff --git a/arch/powerpc/boot/dts/t104xrdb.dtsi=20
> b/arch/powerpc/boot/dts/t104xrdb.dtsi
> index 9aaefa5..e7e765f 100644
> --- a/arch/powerpc/boot/dts/t104xrdb.dtsi
> +++ b/arch/powerpc/boot/dts/t104xrdb.dtsi
> @@ -57,7 +57,8 @@
> };
> =20
> cpld@3,0 {
> - compatible =3D "fsl,t1040rdb-cpld","fsl,t1042rdb-cpld";
> + compatible =3D "fsl,t1040rdb-cpld","fsl,t1042rdb-cpld",
> + "fsl,t1042rdb_pi-cpld";
> reg =3D <3 0 0x300>;
> };
> };
What's going on here? This file is used by all three boards. If you need =
to distinguish one board's CPLD from another's, you'll have to do it somewh=
ere else. If the CPLDs are exactly the same and no distinction needs to be=
made, then you don't need three compatible strings. Even then, you may wi=
sh to specify the exact board as the first compatible string, but again you=
'll need to patch that in elsewhere so that it actually matches the board
.
As the register set of CPLD for all three boards is same, I am thinking of =
replacing this with t104srdb-cpld
compatible =3D "fsl,t104xrdb-cpld","
Is this OK?
-Scott
^ permalink raw reply
* Re: [PATCH v2 5/7] powerpc/corenet: Add MDIO bus muxing support to the board device tree(s)
From: Emil Medve @ 2014-07-31 4:35 UTC (permalink / raw)
To: Scott Wood; +Cc: linuxppc-dev
In-Reply-To: <1406773805.29414.302.camel@snotra.buserror.net>
Hello Scott,
On 07/30/2014 09:30 PM, Scott Wood wrote:
> On Wed, 2014-07-30 at 16:52 -0500, Emil Medve wrote:
>> Hello Scott,
>>
>>
>> On 07/29/2014 02:58 PM, Scott Wood wrote:
>>> On Mon, 2014-07-28 at 06:51 +0000, Emil Medve wrote:
>>>> Hello Scott,
>>>>
>>>>
>>>> Scott Wood <scottwood <at> freescale.com> writes:
>>>>> On Wed, 2014-07-16 at 15:17 -0500, Shruti Kanetkar wrote:
>>>>>> + mdio <at> fd000 {
>>>>>> + /* For 10g interfaces */
>>>>>> + phy_xaui_slot1: xaui-phy <at> slot1 {
>>>>>> + status = "disabled";
>>>>>> + compatible = "ethernet-phy-ieee802.3-c45";
>>>>>> + reg = <0x7>; /* default switch setting on slot1 of AMC2PEX */
>>>>>> + };
>>>>>
>>>>> Why xaui-phy and not ethernet-phy?
>>>>>
>>>>> As for the device_type discussion from v1, there is a generic binding
>>>>> that says device_type "should" be ethernet-phy.
>>>>
>>>> I have no strong feelings about this and we can use ethernet-phy, but:
>>>>
>>>> 1. The binding is old/stale (?) as it still uses device_type and the kernel
>>>> doesn't seem to use anymore the device_type for PHY(s)
>>>
>>> Yes.
>>>
>>>> 2. The binding asks "ethernet-phy" for the device_type property, not for the
>>>> name. As such TBI PHY(s) use (upstream) the tbi-phy@ node name
>>>
>>> It shows ethernet-phy as the name in the example. ePAPR urges generic
>>> node names (this was also a recommendation for IEEE1275), and has
>>> ethernet-phy on the preferred list. Is a xaui-phy not an ethernet phy?
>>
>> So you thinking somebody should cleanup all the sgmii-phy and tbi-phy
>> node names, huh?
>
> No, I was just wondering why we're adding yet another name, and whether
> there's any value in it.
That's fair. We'll just use ethernet-phy
>> It seems that a number of tbi-phy instances slipped by you:
>>
>> 1be62c6 powerpc/mpc85xx: Add BSC9132 QDS Support
>> bf57aeb powerpc/85xx: add the P1020RDB-PD DTS support
>> 8a6be2b powerpc/85xx: Add TWR-P1025 board support
>
> tbi-phy is existing practice. xaui-phy isn't.
>
>>>>>> + mdio0: mdio <at> fc000 {
>>>>>> + };
>>>>>
>>>>> Why is the empty node needed?
>>>>
>>>> For the label
>>>
>>> For mdio-parent-bus, or is there some other dts layer that makes this
>>> node non-empty?
>>
>> 'powerpc/corenet: Create the dts components for the DPAA FMan' -
>> http://patchwork.ozlabs.org/patch/370872
>
> Why does this patch define the mdio0 label for mdio@e1120, but not
> define a label for any other node?
Only MDIO controllers that are pinned out have these labels. Only pinned
out MDIO(s) are capable of controlling external PHY(s) via these board
level MDIO buses
>> and 'powerpc/corenet: Add DPAA
>> FMan support to the SoC device tree(s)' -
>> http://patchwork.ozlabs.org/patch/370868 add content to said node
>
> This one adds content to some mdio nodes, none of which are mdio@fc000
> or &mdio0.
This patch adds the SoC level PHY(s), which in this case are just TBI
PHY(s): i.e. no FMan v2 10 Gb/s MDIO or FMan v3 standalone MDIO devices.
Also the labels become relevant only at board level to connect the MDIO
buses to their corresponding MDIO controllers
Cheers,
^ permalink raw reply
* Re: [2/2] powerpc/fsl-booke: Add initial T1042RDB_PI board support
From: Scott Wood @ 2014-07-31 5:07 UTC (permalink / raw)
To: Jain Priyanka-B32167
Cc: devicetree@vger.kernel.org, linuxppc-dev@lists.ozlabs.org,
Kushwaha Prabhakar-B32579, Aggrwal Poonam-B10812
In-Reply-To: <946e9d7e260c4d78b3c025c3eaeec0f5@BY1PR0301MB0853.namprd03.prod.outlook.com>
On Wed, 2014-07-30 at 23:37 -0500, Jain Priyanka-B32167 wrote:
>
> -----Original Message-----
> From: Wood Scott-B07421
> Sent: Thursday, July 31, 2014 1:43 AM
> To: Jain Priyanka-B32167
> Cc: devicetree@vger.kernel.org; linuxppc-dev@lists.ozlabs.org; Aggrwal Poonam-B10812; Kushwaha Prabhakar-B32579
> Subject: Re: [2/2] powerpc/fsl-booke: Add initial T1042RDB_PI board support
>
> On Wed, Jul 09, 2014 at 09:54:11AM +0530, Priyanka Jain wrote:
> > diff --git a/arch/powerpc/boot/dts/t104xrdb.dtsi
> > b/arch/powerpc/boot/dts/t104xrdb.dtsi
> > index 9aaefa5..e7e765f 100644
> > --- a/arch/powerpc/boot/dts/t104xrdb.dtsi
> > +++ b/arch/powerpc/boot/dts/t104xrdb.dtsi
> > @@ -57,7 +57,8 @@
> > };
> >
> > cpld@3,0 {
> > - compatible = "fsl,t1040rdb-cpld","fsl,t1042rdb-cpld";
> > + compatible = "fsl,t1040rdb-cpld","fsl,t1042rdb-cpld",
> > + "fsl,t1042rdb_pi-cpld";
> > reg = <3 0 0x300>;
> > };
> > };
>
> What's going on here? This file is used by all three boards. If you need to distinguish one board's CPLD from another's, you'll have to do it somewhere else. If the CPLDs are exactly the same and no distinction needs to be made, then you don't need three compatible strings. Even then, you may wish to specify the exact board as the first compatible string, but again you'll need to patch that in elsewhere so that it actually matches the board
> .
> As the register set of CPLD for all three boards is same, I am thinking of replacing this with t104srdb-cpld
> compatible = "fsl,t104xrdb-cpld","
> Is this OK?
No. Wildcards aren't allowed in compatible strings, because you never
know what other devices might exist in the future that match the
wildcard.
If the CPLD logic is truly 100% identical, just pick one of the three to
be the canonical name.
-Scott
^ permalink raw reply
* Re: [PATCH v2 5/7] powerpc/corenet: Add MDIO bus muxing support to the board device tree(s)
From: Scott Wood @ 2014-07-31 5:28 UTC (permalink / raw)
To: Emil Medve; +Cc: linuxppc-dev
In-Reply-To: <53D9C77D.8070203@Freescale.com>
On Wed, 2014-07-30 at 23:35 -0500, Emil Medve wrote:
> Hello Scott,
>
>
> On 07/30/2014 09:30 PM, Scott Wood wrote:
> > On Wed, 2014-07-30 at 16:52 -0500, Emil Medve wrote:
> >>>>>> + mdio0: mdio <at> fc000 {
> >>>>>> + };
> >>>>>
> >>>>> Why is the empty node needed?
> >>>>
> >>>> For the label
> >>>
> >>> For mdio-parent-bus, or is there some other dts layer that makes this
> >>> node non-empty?
> >>
> >> 'powerpc/corenet: Create the dts components for the DPAA FMan' -
> >> http://patchwork.ozlabs.org/patch/370872
> >
> > Why does this patch define the mdio0 label for mdio@e1120, but not
> > define a label for any other node?
>
> Only MDIO controllers that are pinned out have these labels. Only pinned
> out MDIO(s) are capable of controlling external PHY(s) via these board
> level MDIO buses
Is there any reason to describe non-pinned-out MDIO controllers at all?
Is the lack of pinning out inherent to the silicon, or is it board
design/config? Is the answer different for different MDIO controllers?
I'm just curious why mdio@e1120 is labelled in a non-board dtsi while
others are labelled elsewhere.
-Scott
^ permalink raw reply
* Re: [PATCH v2 5/7] powerpc/corenet: Add MDIO bus muxing support to the board device tree(s)
From: Emil Medve @ 2014-07-31 5:48 UTC (permalink / raw)
To: Scott Wood; +Cc: linuxppc-dev
In-Reply-To: <1406784487.29414.328.camel@snotra.buserror.net>
Hello Scott,
On 07/31/2014 12:28 AM, Scott Wood wrote:
> On Wed, 2014-07-30 at 23:35 -0500, Emil Medve wrote:
>> Hello Scott,
>>
>>
>> On 07/30/2014 09:30 PM, Scott Wood wrote:
>>> On Wed, 2014-07-30 at 16:52 -0500, Emil Medve wrote:
>>>>>>>> + mdio0: mdio <at> fc000 {
>>>>>>>> + };
>>>>>>>
>>>>>>> Why is the empty node needed?
>>>>>>
>>>>>> For the label
>>>>>
>>>>> For mdio-parent-bus, or is there some other dts layer that makes this
>>>>> node non-empty?
>>>>
>>>> 'powerpc/corenet: Create the dts components for the DPAA FMan' -
>>>> http://patchwork.ozlabs.org/patch/370872
>>>
>>> Why does this patch define the mdio0 label for mdio@e1120, but not
>>> define a label for any other node?
>>
>> Only MDIO controllers that are pinned out have these labels. Only pinned
>> out MDIO(s) are capable of controlling external PHY(s) via these board
>> level MDIO buses
>
> Is there any reason to describe non-pinned-out MDIO controllers at all?
Yes. For the internal TBI PHY(s). Each MAC supporting SGMII has a TBI
PHY that is attached to the MDIO controller of the respective MAC
> Is the lack of pinning out inherent to the silicon, or is it board
> design/config?
It's a silicon level decision
> Is the answer different for different MDIO controllers?
You mean non-FSL MDIO controllers? Dunno. All FSL SoC have the same MDIO
pin-out decision
> I'm just curious why mdio@e1120 is labelled in a non-board dtsi while
> others are labelled elsewhere.
Labels are relevant only in the context of 'powerpc/corenet: Add MDIO
bus muxing support to the board device tree(s)' -
http://patchwork.ozlabs.org/patch/370866. Most labels are created and
used in the board .dts file except b4qds.dtsi which is shared between
b4420qds.dts and b4860qds.dts
Cheers,
^ permalink raw reply
* Re: [PATCH V7 00/17] Enable SRIOV on POWER8
From: Benjamin Herrenschmidt @ 2014-07-31 6:35 UTC (permalink / raw)
To: bhelgaas; +Cc: Wei Yang, linux-pci, gwshan, qiudayu, yan, linuxppc-dev
In-Reply-To: <1406182947-11302-1-git-send-email-weiyang@linux.vnet.ibm.com>
On Thu, 2014-07-24 at 14:22 +0800, Wei Yang wrote:
> This patch set enables the SRIOV on POWER8.
Hi Bjorn !
There are 4 patches in there to the generic code, but so far not much
review from your side of the fence :-)
How do you want to proceed ?
Cheers,
Ben.
> The gerneral idea is put each VF into one individual PE and allocate required
> resources like DMA/MSI.
>
> One thing special for VF PE is we use M64BT to cover the IOV BAR. M64BT is one
> hardware on POWER platform to map MMIO address to PE. By using M64BT, we could
> map one individual VF to a VF PE, which introduce more flexiblity to users.
>
> To achieve this effect, we need to do some hack on pci devices's resources.
> 1. Expand the IOV BAR properly.
> Done by pnv_pci_ioda_fixup_iov_resources().
> 2. Shift the IOV BAR properly.
> Done by pnv_pci_vf_resource_shift().
> 3. IOV BAR alignment is the total size instead of an individual size on
> powernv platform.
> Done by pnv_pcibios_sriov_resource_alignment().
> 4. Take the IOV BAR alignment into consideration in the sizing and assigning.
> This is achieved by commit: "PCI: Take additional IOV BAR alignment in
> sizing and assigning"
>
> Test Environment:
> The SRIOV device tested is Emulex Lancer and Mellanox ConnectX-3 on
> POWER8.
>
> Examples on pass through a VF to guest through vfio:
> 1. install necessary modules
> modprobe vfio
> modprobe vfio-pci
> 2. retrieve the iommu_group the device belongs to
> readlink /sys/bus/pci/devices/0000:06:0d.0/iommu_group
> ../../../../kernel/iommu_groups/26
> This means it belongs to group 26
> 3. see how many devices under this iommu_group
> ls /sys/kernel/iommu_groups/26/devices/
> 4. unbind the original driver and bind to vfio-pci driver
> echo 0000:06:0d.0 > /sys/bus/pci/devices/0000:06:0d.0/driver/unbind
> echo 1102 0002 > /sys/bus/pci/drivers/vfio-pci/new_id
> Note: this should be done for each device in the same iommu_group
> 5. Start qemu and pass device through vfio
> /home/ywywyang/git/qemu-impreza/ppc64-softmmu/qemu-system-ppc64 \
> -M pseries -m 2048 -enable-kvm -nographic \
> -drive file=/home/ywywyang/kvm/fc19.img \
> -monitor telnet:localhost:5435,server,nowait -boot cd \
> -device "spapr-pci-vfio-host-bridge,id=CXGB3,iommu=26,index=6"
>
> Verify this is the exact VF response:
> 1. ping from a machine in the same subnet(the broadcast domain)
> 2. run arp -n on this machine
> 9.115.251.20 ether 00:00:c9:df:ed:bf C eth0
> 3. ifconfig in the guest
> # ifconfig eth1
> eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
> inet 9.115.251.20 netmask 255.255.255.0 broadcast 9.115.251.255
> inet6 fe80::200:c9ff:fedf:edbf prefixlen 64 scopeid 0x20<link>
> ether 00:00:c9:df:ed:bf txqueuelen 1000 (Ethernet)
> RX packets 175 bytes 13278 (12.9 KiB)
> RX errors 0 dropped 0 overruns 0 frame 0
> TX packets 58 bytes 9276 (9.0 KiB)
> TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
> 4. They have the same MAC address
>
> Note: make sure you shutdown other network interfaces in guest.
>
> ---
> v6 -> v7:
> 1. add IORESOURCE_ARCH flag for IOV BAR on powernv platform.
> 2. when IOV BAR has IORESOURCE_ARCH flag, the size is retrieved from
> hardware directly. If not, calculate as usual.
> 3. reorder the patch set, group them by subsystem:
> PCI, powerpc, powernv
> 4. rebase it on 3.16-rc6
> v5 -> v6:
> 1. remove pcibios_enable_sriov()/pcibios_disable_sriov() weak function
> similar function is moved to
> pnv_pci_enable_device_hook()/pnv_pci_disable_device_hook(). When PF is
> enabled, platform will try best to allocate resources for VFs.
> 2. remove pcibios_sriov_resource_size weak function
> 3. VF BAR size is retrieved from hardware directly in virtfn_add()
> v4 -> v5:
> 1. merge those SRIOV related platform functions in machdep_calls
> wrap them in one CONFIG_PCI_IOV marco
> 2. define IODA_INVALID_M64 to replace (-1)
> use this value to represent the m64_wins is not used
> 3. rename pnv_pci_release_dev_dma() to pnv_pci_ioda2_release_dma_pe()
> this function is a conterpart to pnv_pci_ioda2_setup_dma_pe()
> 4. change dev_info() to dev_dgb() in pnv_pci_ioda_fixup_iov_resources()
> reduce some log in kernel
> 5. release M64 window in pnv_pci_ioda2_release_dma_pe()
> v3 -> v4:
> 1. code format fix, eg. not exceed 80 chars
> 2. in commit "ppc/pnv: Add function to deconfig a PE"
> check the bus has a bridge before print the name
> remove a PE from its own PELTV
> 3. change the function name for sriov resource size/alignment
> 4. rebase on 3.16-rc3
> 5. VFs will not rely on device node
> As Grant Likely's comments, kernel should have the ability to handle the
> lack of device_node gracefully. Gavin restructure the pci_dn, which
> makes the VF will have pci_dn even when VF's device_node is not provided
> by firmware.
> 6. clean all the patch title to make them comply with one style
> 7. fix return value for pci_iov_virtfn_bus/pci_iov_virtfn_devfn
> v2 -> v3:
> 1. change the return type of virtfn_bus/virtfn_devfn to int
> change the name of these two functions to pci_iov_virtfn_bus/pci_iov_virtfn_devfn
> 2. reduce the second parameter or pcibios_sriov_disable()
> 3. use data instead of pe in "ppc/pnv: allocate pe->iommu_table dynamically"
> 4. rename __pci_sriov_resource_size to pcibios_sriov_resource_size
> 5. rename __pci_sriov_resource_alignment to pcibios_sriov_resource_alignment
> v1 -> v2:
> 1. change the return value of virtfn_bus/virtfn_devfn to 0
> 2. move some TCE related marco definition to
> arch/powerpc/platforms/powernv/pci.h
> 3. fix the __pci_sriov_resource_alignment on powernv platform
> During the sizing stage, the IOV BAR is truncated to 0, which will
> effect the order of allocation. Fix this, so that make sure BAR will be
> allocated ordered by their alignment.
> v0 -> v1:
> 1. improve the change log for
> "PCI: Add weak __pci_sriov_resource_size() interface"
> "PCI: Add weak __pci_sriov_resource_alignment() interface"
> "PCI: take additional IOV BAR alignment in sizing and assigning"
> 2. wrap VF PE code in CONFIG_PCI_IOV
> 3. did regression test on P7.
>
> Gavin Shan (2):
> powrepc/pci: Refactor pci_dn
> powerpc/powernv: Use pci_dn in PCI config accessor
>
> Wei Yang (15):
> PCI/IOV: Export interface for retrieve VF's BDF
> PCI/IOV: Get VF BAR size from hardware directly when platform needs
> PCI: Add weak pcibios_sriov_resource_alignment() interface
> PCI: Take additional IOV BAR alignment in sizing and assigning
> powerpc/pci: Don't unset pci resources for VFs
> powerpc/pci: Define pcibios_disable_device() on powerpc
> powerpc/powernv: mark IOV BAR with IORESOURCE_ARCH
> powerpc/powernv: Allocate pe->iommu_table dynamically
> powerpc/powernv: Add function to deconfig a PE
> powerpc/powernv: Expand VF resources according to the number of
> total_pe
> powerpc/powernv: Implement pcibios_sriov_resource_alignment on
> powernv
> powerpc/powernv: Shift VF resource with an offset
> powerpc/powernv: Allocate VF PE
> powerpc/powernv: Expanding IOV BAR, with m64_per_iov supported
> powerpc/powernv: Group VF PE when IOV BAR is big on PHB3
>
> arch/powerpc/include/asm/device.h | 3 +
> arch/powerpc/include/asm/iommu.h | 3 +
> arch/powerpc/include/asm/machdep.h | 12 +-
> arch/powerpc/include/asm/pci-bridge.h | 23 +-
> arch/powerpc/kernel/pci-common.c | 31 ++
> arch/powerpc/kernel/pci-hotplug.c | 3 +
> arch/powerpc/kernel/pci_dn.c | 248 ++++++++-
> arch/powerpc/platforms/powernv/eeh-powernv.c | 24 +-
> arch/powerpc/platforms/powernv/pci-ioda.c | 772 +++++++++++++++++++++++++-
> arch/powerpc/platforms/powernv/pci.c | 107 ++--
> arch/powerpc/platforms/powernv/pci.h | 15 +-
> drivers/pci/iov.c | 65 ++-
> drivers/pci/pci.h | 19 -
> drivers/pci/setup-bus.c | 68 ++-
> include/linux/ioport.h | 1 +
> include/linux/pci.h | 47 ++
> 16 files changed, 1311 insertions(+), 130 deletions(-)
>
^ permalink raw reply
* [patch -next] ASoC: fsl_asrc: fix an error code in fsl_asrc_probe()
From: Dan Carpenter @ 2014-07-31 9:32 UTC (permalink / raw)
To: Timur Tabi
Cc: alsa-devel, Takashi Iwai, kernel-janitors, Liam Girdwood,
Rob Herring, Jaroslav Kysela, Mark Brown, Grant Likely,
linuxppc-dev
There is a cut and paste bug so it returns success instead of the error
code.
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index b9a2888..b04438c 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -826,7 +826,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
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);
+ return PTR_ERR(asrc_priv->mem_clk);
}
asrc_priv->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
^ permalink raw reply related
* [PATCH v2 1/2] printk: Add function to return log buffer address and size
From: Vasant Hegde @ 2014-07-31 10:30 UTC (permalink / raw)
To: linux-kernel; +Cc: Linus Torvalds, Andrew Morton, linuxppc-dev
Platforms like IBM Power Systems supports service processor
assisted dump. It provides interface to add memory region to
be captured when system is crashed.
During initialization/running we can add kernel memory region
to be collected.
Presently we don't have a way to get the log buffer base address
and size. This patch adds support to return log buffer address
and size.
Signed-off-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
---
Next patch extends arch specific code to add log buffer to platform
dump.
-Vasant
include/linux/printk.h | 3 +++
kernel/printk/printk.c | 12 ++++++++++++
2 files changed, 15 insertions(+)
diff --git a/include/linux/printk.h b/include/linux/printk.h
index 319ff7e..aae82c4 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -10,6 +10,9 @@
extern const char linux_banner[];
extern const char linux_proc_banner[];
+extern void *get_log_buf_addr(void);
+extern u32 get_log_buf_len(void);
+
static inline int printk_get_level(const char *buffer)
{
if (buffer[0] == KERN_SOH_ASCII && buffer[1]) {
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 13e839d..4049f7b 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -270,6 +270,18 @@ static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN);
static char *log_buf = __log_buf;
static u32 log_buf_len = __LOG_BUF_LEN;
+/* Return log buffer address */
+void *get_log_buf_addr(void)
+{
+ return log_buf;
+}
+
+/* Return log buffer size */
+u32 get_log_buf_len(void)
+{
+ return log_buf_len;
+}
+
/* human readable text of the record */
static char *log_text(const struct printk_log *msg)
{
^ 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