Including: - Big cleanup of almost unsused parts of the IOMMU API by Christoph Hellwig. This mostly affects the Freescale PAMU driver. - New IOMMU driver for Unisoc SOCs - ARM SMMU Updates from Will: - SMMUv3: Drop vestigial PREFETCH_ADDR support - SMMUv3: Elide TLB sync logic for empty gather - SMMUv3: Fix "Service Failure Mode" handling - SMMUv2: New Qualcomm compatible string - Removal of the AMD IOMMU performance counter writeable check on AMD. It caused long boot delays on some machines and is only needed to work around an errata on some older (possibly pre-production) chips. If someone is still hit by this hardware issue anyway the performance counters will just return 0. - Support for targeted invalidations in the AMD IOMMU driver. Before that the driver only invalidated a single 4k page or the whole IO/TLB for an address space. This has been extended now and is mostly useful for emulated AMD IOMMUs. - Several fixes for the Shared Virtual Memory support in the Intel VT-d driver - Mediatek drivers can now be built as modules - Re-introduction of the forcedac boot option which got lost when converting the Intel VT-d driver to the common dma-iommu implementation. - Extension of the IOMMU device registration interface and support iommu_ops to be const again when drivers are built as modules. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEr9jSbILcajRFYWYyK/BELZcBGuMFAmCMEIoACgkQK/BELZcB GuOu9xAAvg6aR0uHlxvRq6cgNnHN9Ltp5+t3qFYtRRrauY0iOPMO62k0QQli5shX CGeczD0e59KAZqI0zNJnQn8hMY5dg7XVkFCC5BrSzuCDCtwJZ0N5Tq3pfUlaV1rw BJf41t79Fd+jp7kn53tu+vRAfYZ3+sLOx/6U3c15pqKRZSkyFWbQllOtD3J5LnLu 1PyPlfiNpMwCajiS7aQbN+fuJ/lKIFeA2MDPOsCBzhbfxiJUqJxZOKAZO3rOjFfK feTibqQ+3Zz6MPXt9st1cvPpy8jCosv81OY6Knqvxf/oB5q+fEdi2uNrKISonb/t Fw331oOIwg2A+HOpwC9MN1AumOIqiHSWWENAMk9SlP+TMIWKQ8kZreyI6IEB23dV +QvP3DVA+CfLwtNY/Zh0IqKh28D+IHlKbpWNU1m+9AUe468mV/MTjfwxr9Yfffhm LZ6C0DgFdmtqv8jPuDGUOgo3RNeN8bLnUSEHG9gHibA+RKujl5BWDjKkwILqMQTt Ysdsu8TiNtFIULomizqCpgqEbQfW8TLFvASXCM1VMQ/PDURxvchZPxFDJonYXy+K z2HGaG3eUE07YrAdRKH69aMVIbmS+sjEhvmi4xZ1Lh7wWcIE2AZVvO8qNb+Ckcp3 4tLPPDksm/iQngnFf6gdgH3qv4rgbzE4+74GXqeANiQCjY9dSJI= =qF2C -----END PGP SIGNATURE----- Merge tag 'iommu-updates-v5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu Pull iommu updates from Joerg Roedel: - Big cleanup of almost unsused parts of the IOMMU API by Christoph Hellwig. This mostly affects the Freescale PAMU driver. - New IOMMU driver for Unisoc SOCs - ARM SMMU Updates from Will: - Drop vestigial PREFETCH_ADDR support (SMMUv3) - Elide TLB sync logic for empty gather (SMMUv3) - Fix "Service Failure Mode" handling (SMMUv3) - New Qualcomm compatible string (SMMUv2) - Removal of the AMD IOMMU performance counter writeable check on AMD. It caused long boot delays on some machines and is only needed to work around an errata on some older (possibly pre-production) chips. If someone is still hit by this hardware issue anyway the performance counters will just return 0. - Support for targeted invalidations in the AMD IOMMU driver. Before that the driver only invalidated a single 4k page or the whole IO/TLB for an address space. This has been extended now and is mostly useful for emulated AMD IOMMUs. - Several fixes for the Shared Virtual Memory support in the Intel VT-d driver - Mediatek drivers can now be built as modules - Re-introduction of the forcedac boot option which got lost when converting the Intel VT-d driver to the common dma-iommu implementation. - Extension of the IOMMU device registration interface and support iommu_ops to be const again when drivers are built as modules. * tag 'iommu-updates-v5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu: (84 commits) iommu: Streamline registration interface iommu: Statically set module owner iommu/mediatek-v1: Add error handle for mtk_iommu_probe iommu/mediatek-v1: Avoid build fail when build as module iommu/mediatek: Always enable the clk on resume iommu/fsl-pamu: Fix uninitialized variable warning iommu/vt-d: Force to flush iotlb before creating superpage iommu/amd: Put newline after closing bracket in warning iommu/vt-d: Fix an error handling path in 'intel_prepare_irq_remapping()' iommu/vt-d: Fix build error of pasid_enable_wpe() with !X86 iommu/amd: Remove performance counter pre-initialization test Revert "iommu/amd: Fix performance counter initialization" iommu/amd: Remove duplicate check of devid iommu/exynos: Remove unneeded local variable initialization iommu/amd: Page-specific invalidations for more than one page iommu/arm-smmu-v3: Remove the unused fields for PREFETCH_CONFIG command iommu/vt-d: Avoid unnecessary cache flush in pasid entry teardown iommu/vt-d: Invalidate PASID cache when root/context entry changed iommu/vt-d: Remove WO permissions on second-level paging entries iommu/vt-d: Report the right page fault address ...
342 lines
8.7 KiB
C
342 lines
8.7 KiB
C
/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of Freescale Semiconductor nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* ALTERNATIVELY, this software may be distributed under the terms of the
|
|
* GNU General Public License ("GPL") as published by the Free Software
|
|
* Foundation, either version 2 of that License or (at your option) any
|
|
* later version.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "qman_priv.h"
|
|
|
|
struct qman_portal *qman_dma_portal;
|
|
EXPORT_SYMBOL(qman_dma_portal);
|
|
|
|
/* Enable portal interupts (as opposed to polling mode) */
|
|
#define CONFIG_FSL_DPA_PIRQ_SLOW 1
|
|
#define CONFIG_FSL_DPA_PIRQ_FAST 1
|
|
|
|
static struct cpumask portal_cpus;
|
|
static int __qman_portals_probed;
|
|
/* protect qman global registers and global data shared among portals */
|
|
static DEFINE_SPINLOCK(qman_lock);
|
|
|
|
static void portal_set_cpu(struct qm_portal_config *pcfg, int cpu)
|
|
{
|
|
#ifdef CONFIG_FSL_PAMU
|
|
struct device *dev = pcfg->dev;
|
|
int ret;
|
|
|
|
pcfg->iommu_domain = iommu_domain_alloc(&platform_bus_type);
|
|
if (!pcfg->iommu_domain) {
|
|
dev_err(dev, "%s(): iommu_domain_alloc() failed", __func__);
|
|
goto no_iommu;
|
|
}
|
|
ret = fsl_pamu_configure_l1_stash(pcfg->iommu_domain, cpu);
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s(): fsl_pamu_configure_l1_stash() = %d",
|
|
__func__, ret);
|
|
goto out_domain_free;
|
|
}
|
|
ret = iommu_attach_device(pcfg->iommu_domain, dev);
|
|
if (ret < 0) {
|
|
dev_err(dev, "%s(): iommu_device_attach() = %d", __func__,
|
|
ret);
|
|
goto out_domain_free;
|
|
}
|
|
|
|
no_iommu:
|
|
#endif
|
|
qman_set_sdest(pcfg->channel, cpu);
|
|
|
|
return;
|
|
|
|
#ifdef CONFIG_FSL_PAMU
|
|
out_domain_free:
|
|
iommu_domain_free(pcfg->iommu_domain);
|
|
pcfg->iommu_domain = NULL;
|
|
#endif
|
|
}
|
|
|
|
static struct qman_portal *init_pcfg(struct qm_portal_config *pcfg)
|
|
{
|
|
struct qman_portal *p;
|
|
u32 irq_sources = 0;
|
|
|
|
/* We need the same LIODN offset for all portals */
|
|
qman_liodn_fixup(pcfg->channel);
|
|
|
|
pcfg->iommu_domain = NULL;
|
|
portal_set_cpu(pcfg, pcfg->cpu);
|
|
|
|
p = qman_create_affine_portal(pcfg, NULL);
|
|
if (!p) {
|
|
dev_crit(pcfg->dev, "%s: Portal failure on cpu %d\n",
|
|
__func__, pcfg->cpu);
|
|
return NULL;
|
|
}
|
|
|
|
/* Determine what should be interrupt-vs-poll driven */
|
|
#ifdef CONFIG_FSL_DPA_PIRQ_SLOW
|
|
irq_sources |= QM_PIRQ_EQCI | QM_PIRQ_EQRI | QM_PIRQ_MRI |
|
|
QM_PIRQ_CSCI;
|
|
#endif
|
|
#ifdef CONFIG_FSL_DPA_PIRQ_FAST
|
|
irq_sources |= QM_PIRQ_DQRI;
|
|
#endif
|
|
qman_p_irqsource_add(p, irq_sources);
|
|
|
|
spin_lock(&qman_lock);
|
|
if (cpumask_equal(&portal_cpus, cpu_possible_mask)) {
|
|
/* all assigned portals are initialized now */
|
|
qman_init_cgr_all();
|
|
}
|
|
|
|
if (!qman_dma_portal)
|
|
qman_dma_portal = p;
|
|
|
|
spin_unlock(&qman_lock);
|
|
|
|
dev_info(pcfg->dev, "Portal initialised, cpu %d\n", pcfg->cpu);
|
|
|
|
return p;
|
|
}
|
|
|
|
static void qman_portal_update_sdest(const struct qm_portal_config *pcfg,
|
|
unsigned int cpu)
|
|
{
|
|
#ifdef CONFIG_FSL_PAMU /* TODO */
|
|
if (pcfg->iommu_domain) {
|
|
if (fsl_pamu_configure_l1_stash(pcfg->iommu_domain, cpu) < 0) {
|
|
dev_err(pcfg->dev,
|
|
"Failed to update pamu stash setting\n");
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
qman_set_sdest(pcfg->channel, cpu);
|
|
}
|
|
|
|
static int qman_offline_cpu(unsigned int cpu)
|
|
{
|
|
struct qman_portal *p;
|
|
const struct qm_portal_config *pcfg;
|
|
|
|
p = affine_portals[cpu];
|
|
if (p) {
|
|
pcfg = qman_get_qm_portal_config(p);
|
|
if (pcfg) {
|
|
/* select any other online CPU */
|
|
cpu = cpumask_any_but(cpu_online_mask, cpu);
|
|
irq_set_affinity(pcfg->irq, cpumask_of(cpu));
|
|
qman_portal_update_sdest(pcfg, cpu);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int qman_online_cpu(unsigned int cpu)
|
|
{
|
|
struct qman_portal *p;
|
|
const struct qm_portal_config *pcfg;
|
|
|
|
p = affine_portals[cpu];
|
|
if (p) {
|
|
pcfg = qman_get_qm_portal_config(p);
|
|
if (pcfg) {
|
|
irq_set_affinity(pcfg->irq, cpumask_of(cpu));
|
|
qman_portal_update_sdest(pcfg, cpu);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int qman_portals_probed(void)
|
|
{
|
|
return __qman_portals_probed;
|
|
}
|
|
EXPORT_SYMBOL_GPL(qman_portals_probed);
|
|
|
|
static int qman_portal_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *node = dev->of_node;
|
|
struct qm_portal_config *pcfg;
|
|
struct resource *addr_phys[2];
|
|
int irq, cpu, err, i;
|
|
u32 val;
|
|
|
|
err = qman_is_probed();
|
|
if (!err)
|
|
return -EPROBE_DEFER;
|
|
if (err < 0) {
|
|
dev_err(&pdev->dev, "failing probe due to qman probe error\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
pcfg = devm_kmalloc(dev, sizeof(*pcfg), GFP_KERNEL);
|
|
if (!pcfg) {
|
|
__qman_portals_probed = -1;
|
|
return -ENOMEM;
|
|
}
|
|
|
|
pcfg->dev = dev;
|
|
|
|
addr_phys[0] = platform_get_resource(pdev, IORESOURCE_MEM,
|
|
DPAA_PORTAL_CE);
|
|
if (!addr_phys[0]) {
|
|
dev_err(dev, "Can't get %pOF property 'reg::CE'\n", node);
|
|
goto err_ioremap1;
|
|
}
|
|
|
|
addr_phys[1] = platform_get_resource(pdev, IORESOURCE_MEM,
|
|
DPAA_PORTAL_CI);
|
|
if (!addr_phys[1]) {
|
|
dev_err(dev, "Can't get %pOF property 'reg::CI'\n", node);
|
|
goto err_ioremap1;
|
|
}
|
|
|
|
err = of_property_read_u32(node, "cell-index", &val);
|
|
if (err) {
|
|
dev_err(dev, "Can't get %pOF property 'cell-index'\n", node);
|
|
__qman_portals_probed = -1;
|
|
return err;
|
|
}
|
|
pcfg->channel = val;
|
|
pcfg->cpu = -1;
|
|
irq = platform_get_irq(pdev, 0);
|
|
if (irq <= 0)
|
|
goto err_ioremap1;
|
|
pcfg->irq = irq;
|
|
|
|
pcfg->addr_virt_ce = memremap(addr_phys[0]->start,
|
|
resource_size(addr_phys[0]),
|
|
QBMAN_MEMREMAP_ATTR);
|
|
if (!pcfg->addr_virt_ce) {
|
|
dev_err(dev, "memremap::CE failed\n");
|
|
goto err_ioremap1;
|
|
}
|
|
|
|
pcfg->addr_virt_ci = ioremap(addr_phys[1]->start,
|
|
resource_size(addr_phys[1]));
|
|
if (!pcfg->addr_virt_ci) {
|
|
dev_err(dev, "ioremap::CI failed\n");
|
|
goto err_ioremap2;
|
|
}
|
|
|
|
pcfg->pools = qm_get_pools_sdqcr();
|
|
|
|
spin_lock(&qman_lock);
|
|
cpu = cpumask_next_zero(-1, &portal_cpus);
|
|
if (cpu >= nr_cpu_ids) {
|
|
__qman_portals_probed = 1;
|
|
/* unassigned portal, skip init */
|
|
spin_unlock(&qman_lock);
|
|
goto check_cleanup;
|
|
}
|
|
|
|
cpumask_set_cpu(cpu, &portal_cpus);
|
|
spin_unlock(&qman_lock);
|
|
pcfg->cpu = cpu;
|
|
|
|
if (dma_set_mask(dev, DMA_BIT_MASK(40))) {
|
|
dev_err(dev, "dma_set_mask() failed\n");
|
|
goto err_portal_init;
|
|
}
|
|
|
|
if (!init_pcfg(pcfg)) {
|
|
dev_err(dev, "portal init failed\n");
|
|
goto err_portal_init;
|
|
}
|
|
|
|
/* clear irq affinity if assigned cpu is offline */
|
|
if (!cpu_online(cpu))
|
|
qman_offline_cpu(cpu);
|
|
|
|
check_cleanup:
|
|
if (__qman_portals_probed == 1 && qman_requires_cleanup()) {
|
|
/*
|
|
* QMan wasn't reset prior to boot (Kexec for example)
|
|
* Empty all the frame queues so they are in reset state
|
|
*/
|
|
for (i = 0; i < qm_get_fqid_maxcnt(); i++) {
|
|
err = qman_shutdown_fq(i);
|
|
if (err) {
|
|
dev_err(dev, "Failed to shutdown frame queue %d\n",
|
|
i);
|
|
goto err_portal_init;
|
|
}
|
|
}
|
|
qman_done_cleanup();
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_portal_init:
|
|
iounmap(pcfg->addr_virt_ci);
|
|
err_ioremap2:
|
|
memunmap(pcfg->addr_virt_ce);
|
|
err_ioremap1:
|
|
__qman_portals_probed = -1;
|
|
|
|
return -ENXIO;
|
|
}
|
|
|
|
static const struct of_device_id qman_portal_ids[] = {
|
|
{
|
|
.compatible = "fsl,qman-portal",
|
|
},
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(of, qman_portal_ids);
|
|
|
|
static struct platform_driver qman_portal_driver = {
|
|
.driver = {
|
|
.name = KBUILD_MODNAME,
|
|
.of_match_table = qman_portal_ids,
|
|
},
|
|
.probe = qman_portal_probe,
|
|
};
|
|
|
|
static int __init qman_portal_driver_register(struct platform_driver *drv)
|
|
{
|
|
int ret;
|
|
|
|
ret = platform_driver_register(drv);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
|
|
"soc/qman_portal:online",
|
|
qman_online_cpu, qman_offline_cpu);
|
|
if (ret < 0) {
|
|
pr_err("qman: failed to register hotplug callbacks.\n");
|
|
platform_driver_unregister(drv);
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
module_driver(qman_portal_driver,
|
|
qman_portal_driver_register, platform_driver_unregister);
|