162306a36Sopenharmony_ci/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
262306a36Sopenharmony_ci *
362306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
462306a36Sopenharmony_ci * modification, are permitted provided that the following conditions are met:
562306a36Sopenharmony_ci *     * Redistributions of source code must retain the above copyright
662306a36Sopenharmony_ci *	 notice, this list of conditions and the following disclaimer.
762306a36Sopenharmony_ci *     * Redistributions in binary form must reproduce the above copyright
862306a36Sopenharmony_ci *	 notice, this list of conditions and the following disclaimer in the
962306a36Sopenharmony_ci *	 documentation and/or other materials provided with the distribution.
1062306a36Sopenharmony_ci *     * Neither the name of Freescale Semiconductor nor the
1162306a36Sopenharmony_ci *	 names of its contributors may be used to endorse or promote products
1262306a36Sopenharmony_ci *	 derived from this software without specific prior written permission.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * ALTERNATIVELY, this software may be distributed under the terms of the
1562306a36Sopenharmony_ci * GNU General Public License ("GPL") as published by the Free Software
1662306a36Sopenharmony_ci * Foundation, either version 2 of that License or (at your option) any
1762306a36Sopenharmony_ci * later version.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
2062306a36Sopenharmony_ci * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2162306a36Sopenharmony_ci * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2262306a36Sopenharmony_ci * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
2362306a36Sopenharmony_ci * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2462306a36Sopenharmony_ci * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2562306a36Sopenharmony_ci * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2662306a36Sopenharmony_ci * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2762306a36Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2862306a36Sopenharmony_ci * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include "qman_priv.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistruct qman_portal *qman_dma_portal;
3462306a36Sopenharmony_ciEXPORT_SYMBOL(qman_dma_portal);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/* Enable portal interupts (as opposed to polling mode) */
3762306a36Sopenharmony_ci#define CONFIG_FSL_DPA_PIRQ_SLOW  1
3862306a36Sopenharmony_ci#define CONFIG_FSL_DPA_PIRQ_FAST  1
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic struct cpumask portal_cpus;
4162306a36Sopenharmony_cistatic int __qman_portals_probed;
4262306a36Sopenharmony_ci/* protect qman global registers and global data shared among portals */
4362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(qman_lock);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic void portal_set_cpu(struct qm_portal_config *pcfg, int cpu)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci#ifdef CONFIG_FSL_PAMU
4862306a36Sopenharmony_ci	struct device *dev = pcfg->dev;
4962306a36Sopenharmony_ci	int ret;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	pcfg->iommu_domain = iommu_domain_alloc(&platform_bus_type);
5262306a36Sopenharmony_ci	if (!pcfg->iommu_domain) {
5362306a36Sopenharmony_ci		dev_err(dev, "%s(): iommu_domain_alloc() failed", __func__);
5462306a36Sopenharmony_ci		goto no_iommu;
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci	ret = fsl_pamu_configure_l1_stash(pcfg->iommu_domain, cpu);
5762306a36Sopenharmony_ci	if (ret < 0) {
5862306a36Sopenharmony_ci		dev_err(dev, "%s(): fsl_pamu_configure_l1_stash() = %d",
5962306a36Sopenharmony_ci			__func__, ret);
6062306a36Sopenharmony_ci		goto out_domain_free;
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci	ret = iommu_attach_device(pcfg->iommu_domain, dev);
6362306a36Sopenharmony_ci	if (ret < 0) {
6462306a36Sopenharmony_ci		dev_err(dev, "%s(): iommu_device_attach() = %d", __func__,
6562306a36Sopenharmony_ci			ret);
6662306a36Sopenharmony_ci		goto out_domain_free;
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cino_iommu:
7062306a36Sopenharmony_ci#endif
7162306a36Sopenharmony_ci	qman_set_sdest(pcfg->channel, cpu);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#ifdef CONFIG_FSL_PAMU
7662306a36Sopenharmony_ciout_domain_free:
7762306a36Sopenharmony_ci	iommu_domain_free(pcfg->iommu_domain);
7862306a36Sopenharmony_ci	pcfg->iommu_domain = NULL;
7962306a36Sopenharmony_ci#endif
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic struct qman_portal *init_pcfg(struct qm_portal_config *pcfg)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	struct qman_portal *p;
8562306a36Sopenharmony_ci	u32 irq_sources = 0;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/* We need the same LIODN offset for all portals */
8862306a36Sopenharmony_ci	qman_liodn_fixup(pcfg->channel);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	pcfg->iommu_domain = NULL;
9162306a36Sopenharmony_ci	portal_set_cpu(pcfg, pcfg->cpu);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	p = qman_create_affine_portal(pcfg, NULL);
9462306a36Sopenharmony_ci	if (!p) {
9562306a36Sopenharmony_ci		dev_crit(pcfg->dev, "%s: Portal failure on cpu %d\n",
9662306a36Sopenharmony_ci			 __func__, pcfg->cpu);
9762306a36Sopenharmony_ci		return NULL;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	/* Determine what should be interrupt-vs-poll driven */
10162306a36Sopenharmony_ci#ifdef CONFIG_FSL_DPA_PIRQ_SLOW
10262306a36Sopenharmony_ci	irq_sources |= QM_PIRQ_EQCI | QM_PIRQ_EQRI | QM_PIRQ_MRI |
10362306a36Sopenharmony_ci		       QM_PIRQ_CSCI;
10462306a36Sopenharmony_ci#endif
10562306a36Sopenharmony_ci#ifdef CONFIG_FSL_DPA_PIRQ_FAST
10662306a36Sopenharmony_ci	irq_sources |= QM_PIRQ_DQRI;
10762306a36Sopenharmony_ci#endif
10862306a36Sopenharmony_ci	qman_p_irqsource_add(p, irq_sources);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	spin_lock(&qman_lock);
11162306a36Sopenharmony_ci	if (cpumask_equal(&portal_cpus, cpu_possible_mask)) {
11262306a36Sopenharmony_ci		/* all assigned portals are initialized now */
11362306a36Sopenharmony_ci		qman_init_cgr_all();
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (!qman_dma_portal)
11762306a36Sopenharmony_ci		qman_dma_portal = p;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	spin_unlock(&qman_lock);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	dev_info(pcfg->dev, "Portal initialised, cpu %d\n", pcfg->cpu);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return p;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic void qman_portal_update_sdest(const struct qm_portal_config *pcfg,
12762306a36Sopenharmony_ci							unsigned int cpu)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci#ifdef CONFIG_FSL_PAMU /* TODO */
13062306a36Sopenharmony_ci	if (pcfg->iommu_domain) {
13162306a36Sopenharmony_ci		if (fsl_pamu_configure_l1_stash(pcfg->iommu_domain, cpu) < 0) {
13262306a36Sopenharmony_ci			dev_err(pcfg->dev,
13362306a36Sopenharmony_ci				"Failed to update pamu stash setting\n");
13462306a36Sopenharmony_ci			return;
13562306a36Sopenharmony_ci		}
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci#endif
13862306a36Sopenharmony_ci	qman_set_sdest(pcfg->channel, cpu);
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic int qman_offline_cpu(unsigned int cpu)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	struct qman_portal *p;
14462306a36Sopenharmony_ci	const struct qm_portal_config *pcfg;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	p = affine_portals[cpu];
14762306a36Sopenharmony_ci	if (p) {
14862306a36Sopenharmony_ci		pcfg = qman_get_qm_portal_config(p);
14962306a36Sopenharmony_ci		if (pcfg) {
15062306a36Sopenharmony_ci			/* select any other online CPU */
15162306a36Sopenharmony_ci			cpu = cpumask_any_but(cpu_online_mask, cpu);
15262306a36Sopenharmony_ci			irq_set_affinity(pcfg->irq, cpumask_of(cpu));
15362306a36Sopenharmony_ci			qman_portal_update_sdest(pcfg, cpu);
15462306a36Sopenharmony_ci		}
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci	return 0;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic int qman_online_cpu(unsigned int cpu)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	struct qman_portal *p;
16262306a36Sopenharmony_ci	const struct qm_portal_config *pcfg;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	p = affine_portals[cpu];
16562306a36Sopenharmony_ci	if (p) {
16662306a36Sopenharmony_ci		pcfg = qman_get_qm_portal_config(p);
16762306a36Sopenharmony_ci		if (pcfg) {
16862306a36Sopenharmony_ci			irq_set_affinity(pcfg->irq, cpumask_of(cpu));
16962306a36Sopenharmony_ci			qman_portal_update_sdest(pcfg, cpu);
17062306a36Sopenharmony_ci		}
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci	return 0;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ciint qman_portals_probed(void)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	return __qman_portals_probed;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qman_portals_probed);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic int qman_portal_probe(struct platform_device *pdev)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
18462306a36Sopenharmony_ci	struct device_node *node = dev->of_node;
18562306a36Sopenharmony_ci	struct qm_portal_config *pcfg;
18662306a36Sopenharmony_ci	struct resource *addr_phys[2];
18762306a36Sopenharmony_ci	int irq, cpu, err, i;
18862306a36Sopenharmony_ci	u32 val;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	err = qman_is_probed();
19162306a36Sopenharmony_ci	if (!err)
19262306a36Sopenharmony_ci		return -EPROBE_DEFER;
19362306a36Sopenharmony_ci	if (err < 0) {
19462306a36Sopenharmony_ci		dev_err(&pdev->dev, "failing probe due to qman probe error\n");
19562306a36Sopenharmony_ci		return -ENODEV;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	pcfg = devm_kmalloc(dev, sizeof(*pcfg), GFP_KERNEL);
19962306a36Sopenharmony_ci	if (!pcfg) {
20062306a36Sopenharmony_ci		__qman_portals_probed = -1;
20162306a36Sopenharmony_ci		return -ENOMEM;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	pcfg->dev = dev;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	addr_phys[0] = platform_get_resource(pdev, IORESOURCE_MEM,
20762306a36Sopenharmony_ci					     DPAA_PORTAL_CE);
20862306a36Sopenharmony_ci	if (!addr_phys[0]) {
20962306a36Sopenharmony_ci		dev_err(dev, "Can't get %pOF property 'reg::CE'\n", node);
21062306a36Sopenharmony_ci		goto err_ioremap1;
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	addr_phys[1] = platform_get_resource(pdev, IORESOURCE_MEM,
21462306a36Sopenharmony_ci					     DPAA_PORTAL_CI);
21562306a36Sopenharmony_ci	if (!addr_phys[1]) {
21662306a36Sopenharmony_ci		dev_err(dev, "Can't get %pOF property 'reg::CI'\n", node);
21762306a36Sopenharmony_ci		goto err_ioremap1;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	err = of_property_read_u32(node, "cell-index", &val);
22162306a36Sopenharmony_ci	if (err) {
22262306a36Sopenharmony_ci		dev_err(dev, "Can't get %pOF property 'cell-index'\n", node);
22362306a36Sopenharmony_ci		__qman_portals_probed = -1;
22462306a36Sopenharmony_ci		return err;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci	pcfg->channel = val;
22762306a36Sopenharmony_ci	pcfg->cpu = -1;
22862306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
22962306a36Sopenharmony_ci	if (irq <= 0)
23062306a36Sopenharmony_ci		goto err_ioremap1;
23162306a36Sopenharmony_ci	pcfg->irq = irq;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	pcfg->addr_virt_ce = memremap(addr_phys[0]->start,
23462306a36Sopenharmony_ci					resource_size(addr_phys[0]),
23562306a36Sopenharmony_ci					QBMAN_MEMREMAP_ATTR);
23662306a36Sopenharmony_ci	if (!pcfg->addr_virt_ce) {
23762306a36Sopenharmony_ci		dev_err(dev, "memremap::CE failed\n");
23862306a36Sopenharmony_ci		goto err_ioremap1;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	pcfg->addr_virt_ci = ioremap(addr_phys[1]->start,
24262306a36Sopenharmony_ci				resource_size(addr_phys[1]));
24362306a36Sopenharmony_ci	if (!pcfg->addr_virt_ci) {
24462306a36Sopenharmony_ci		dev_err(dev, "ioremap::CI failed\n");
24562306a36Sopenharmony_ci		goto err_ioremap2;
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	pcfg->pools = qm_get_pools_sdqcr();
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	spin_lock(&qman_lock);
25162306a36Sopenharmony_ci	cpu = cpumask_first_zero(&portal_cpus);
25262306a36Sopenharmony_ci	if (cpu >= nr_cpu_ids) {
25362306a36Sopenharmony_ci		__qman_portals_probed = 1;
25462306a36Sopenharmony_ci		/* unassigned portal, skip init */
25562306a36Sopenharmony_ci		spin_unlock(&qman_lock);
25662306a36Sopenharmony_ci		goto check_cleanup;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	cpumask_set_cpu(cpu, &portal_cpus);
26062306a36Sopenharmony_ci	spin_unlock(&qman_lock);
26162306a36Sopenharmony_ci	pcfg->cpu = cpu;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (dma_set_mask(dev, DMA_BIT_MASK(40))) {
26462306a36Sopenharmony_ci		dev_err(dev, "dma_set_mask() failed\n");
26562306a36Sopenharmony_ci		goto err_portal_init;
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (!init_pcfg(pcfg)) {
26962306a36Sopenharmony_ci		dev_err(dev, "portal init failed\n");
27062306a36Sopenharmony_ci		goto err_portal_init;
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/* clear irq affinity if assigned cpu is offline */
27462306a36Sopenharmony_ci	if (!cpu_online(cpu))
27562306a36Sopenharmony_ci		qman_offline_cpu(cpu);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cicheck_cleanup:
27862306a36Sopenharmony_ci	if (__qman_portals_probed == 1 && qman_requires_cleanup()) {
27962306a36Sopenharmony_ci		/*
28062306a36Sopenharmony_ci		 * QMan wasn't reset prior to boot (Kexec for example)
28162306a36Sopenharmony_ci		 * Empty all the frame queues so they are in reset state
28262306a36Sopenharmony_ci		 */
28362306a36Sopenharmony_ci		for (i = 0; i < qm_get_fqid_maxcnt(); i++) {
28462306a36Sopenharmony_ci			err =  qman_shutdown_fq(i);
28562306a36Sopenharmony_ci			if (err) {
28662306a36Sopenharmony_ci				dev_err(dev, "Failed to shutdown frame queue %d\n",
28762306a36Sopenharmony_ci					i);
28862306a36Sopenharmony_ci				goto err_portal_init;
28962306a36Sopenharmony_ci			}
29062306a36Sopenharmony_ci		}
29162306a36Sopenharmony_ci		qman_done_cleanup();
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	return 0;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cierr_portal_init:
29762306a36Sopenharmony_ci	iounmap(pcfg->addr_virt_ci);
29862306a36Sopenharmony_cierr_ioremap2:
29962306a36Sopenharmony_ci	memunmap(pcfg->addr_virt_ce);
30062306a36Sopenharmony_cierr_ioremap1:
30162306a36Sopenharmony_ci	__qman_portals_probed = -1;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	return -ENXIO;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic const struct of_device_id qman_portal_ids[] = {
30762306a36Sopenharmony_ci	{
30862306a36Sopenharmony_ci		.compatible = "fsl,qman-portal",
30962306a36Sopenharmony_ci	},
31062306a36Sopenharmony_ci	{}
31162306a36Sopenharmony_ci};
31262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, qman_portal_ids);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic struct platform_driver qman_portal_driver = {
31562306a36Sopenharmony_ci	.driver = {
31662306a36Sopenharmony_ci		.name = KBUILD_MODNAME,
31762306a36Sopenharmony_ci		.of_match_table = qman_portal_ids,
31862306a36Sopenharmony_ci	},
31962306a36Sopenharmony_ci	.probe = qman_portal_probe,
32062306a36Sopenharmony_ci};
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic int __init qman_portal_driver_register(struct platform_driver *drv)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	int ret;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	ret = platform_driver_register(drv);
32762306a36Sopenharmony_ci	if (ret < 0)
32862306a36Sopenharmony_ci		return ret;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
33162306a36Sopenharmony_ci					"soc/qman_portal:online",
33262306a36Sopenharmony_ci					qman_online_cpu, qman_offline_cpu);
33362306a36Sopenharmony_ci	if (ret < 0) {
33462306a36Sopenharmony_ci		pr_err("qman: failed to register hotplug callbacks.\n");
33562306a36Sopenharmony_ci		platform_driver_unregister(drv);
33662306a36Sopenharmony_ci		return ret;
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci	return 0;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cimodule_driver(qman_portal_driver,
34262306a36Sopenharmony_ci	      qman_portal_driver_register, platform_driver_unregister);
343