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 "bman_priv.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic struct bman_portal *affine_bportals[NR_CPUS];
3462306a36Sopenharmony_cistatic struct cpumask portal_cpus;
3562306a36Sopenharmony_cistatic int __bman_portals_probed;
3662306a36Sopenharmony_ci/* protect bman global registers and global data shared among portals */
3762306a36Sopenharmony_cistatic DEFINE_SPINLOCK(bman_lock);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic struct bman_portal *init_pcfg(struct bm_portal_config *pcfg)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct bman_portal *p = bman_create_affine_portal(pcfg);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	if (!p) {
4462306a36Sopenharmony_ci		dev_crit(pcfg->dev, "%s: Portal failure on cpu %d\n",
4562306a36Sopenharmony_ci			 __func__, pcfg->cpu);
4662306a36Sopenharmony_ci		return NULL;
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	bman_p_irqsource_add(p, BM_PIRQ_RCRI);
5062306a36Sopenharmony_ci	affine_bportals[pcfg->cpu] = p;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	dev_info(pcfg->dev, "Portal initialised, cpu %d\n", pcfg->cpu);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return p;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic int bman_offline_cpu(unsigned int cpu)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct bman_portal *p = affine_bportals[cpu];
6062306a36Sopenharmony_ci	const struct bm_portal_config *pcfg;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (!p)
6362306a36Sopenharmony_ci		return 0;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	pcfg = bman_get_bm_portal_config(p);
6662306a36Sopenharmony_ci	if (!pcfg)
6762306a36Sopenharmony_ci		return 0;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	/* use any other online CPU */
7062306a36Sopenharmony_ci	cpu = cpumask_any_but(cpu_online_mask, cpu);
7162306a36Sopenharmony_ci	irq_set_affinity(pcfg->irq, cpumask_of(cpu));
7262306a36Sopenharmony_ci	return 0;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int bman_online_cpu(unsigned int cpu)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct bman_portal *p = affine_bportals[cpu];
7862306a36Sopenharmony_ci	const struct bm_portal_config *pcfg;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (!p)
8162306a36Sopenharmony_ci		return 0;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	pcfg = bman_get_bm_portal_config(p);
8462306a36Sopenharmony_ci	if (!pcfg)
8562306a36Sopenharmony_ci		return 0;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	irq_set_affinity(pcfg->irq, cpumask_of(cpu));
8862306a36Sopenharmony_ci	return 0;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ciint bman_portals_probed(void)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	return __bman_portals_probed;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bman_portals_probed);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic int bman_portal_probe(struct platform_device *pdev)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
10062306a36Sopenharmony_ci	struct device_node *node = dev->of_node;
10162306a36Sopenharmony_ci	struct bm_portal_config *pcfg;
10262306a36Sopenharmony_ci	struct resource *addr_phys[2];
10362306a36Sopenharmony_ci	int irq, cpu, err, i;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	err = bman_is_probed();
10662306a36Sopenharmony_ci	if (!err)
10762306a36Sopenharmony_ci		return -EPROBE_DEFER;
10862306a36Sopenharmony_ci	if (err < 0) {
10962306a36Sopenharmony_ci		dev_err(&pdev->dev, "failing probe due to bman probe error\n");
11062306a36Sopenharmony_ci		return -ENODEV;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	pcfg = devm_kmalloc(dev, sizeof(*pcfg), GFP_KERNEL);
11462306a36Sopenharmony_ci	if (!pcfg) {
11562306a36Sopenharmony_ci		__bman_portals_probed = -1;
11662306a36Sopenharmony_ci		return -ENOMEM;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	pcfg->dev = dev;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	addr_phys[0] = platform_get_resource(pdev, IORESOURCE_MEM,
12262306a36Sopenharmony_ci					     DPAA_PORTAL_CE);
12362306a36Sopenharmony_ci	if (!addr_phys[0]) {
12462306a36Sopenharmony_ci		dev_err(dev, "Can't get %pOF property 'reg::CE'\n", node);
12562306a36Sopenharmony_ci		goto err_ioremap1;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	addr_phys[1] = platform_get_resource(pdev, IORESOURCE_MEM,
12962306a36Sopenharmony_ci					     DPAA_PORTAL_CI);
13062306a36Sopenharmony_ci	if (!addr_phys[1]) {
13162306a36Sopenharmony_ci		dev_err(dev, "Can't get %pOF property 'reg::CI'\n", node);
13262306a36Sopenharmony_ci		goto err_ioremap1;
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	pcfg->cpu = -1;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
13862306a36Sopenharmony_ci	if (irq <= 0)
13962306a36Sopenharmony_ci		goto err_ioremap1;
14062306a36Sopenharmony_ci	pcfg->irq = irq;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	pcfg->addr_virt_ce = memremap(addr_phys[0]->start,
14362306a36Sopenharmony_ci					resource_size(addr_phys[0]),
14462306a36Sopenharmony_ci					QBMAN_MEMREMAP_ATTR);
14562306a36Sopenharmony_ci	if (!pcfg->addr_virt_ce) {
14662306a36Sopenharmony_ci		dev_err(dev, "memremap::CE failed\n");
14762306a36Sopenharmony_ci		goto err_ioremap1;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	pcfg->addr_virt_ci = ioremap(addr_phys[1]->start,
15162306a36Sopenharmony_ci					resource_size(addr_phys[1]));
15262306a36Sopenharmony_ci	if (!pcfg->addr_virt_ci) {
15362306a36Sopenharmony_ci		dev_err(dev, "ioremap::CI failed\n");
15462306a36Sopenharmony_ci		goto err_ioremap2;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	spin_lock(&bman_lock);
15862306a36Sopenharmony_ci	cpu = cpumask_first_zero(&portal_cpus);
15962306a36Sopenharmony_ci	if (cpu >= nr_cpu_ids) {
16062306a36Sopenharmony_ci		__bman_portals_probed = 1;
16162306a36Sopenharmony_ci		/* unassigned portal, skip init */
16262306a36Sopenharmony_ci		spin_unlock(&bman_lock);
16362306a36Sopenharmony_ci		goto check_cleanup;
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	cpumask_set_cpu(cpu, &portal_cpus);
16762306a36Sopenharmony_ci	spin_unlock(&bman_lock);
16862306a36Sopenharmony_ci	pcfg->cpu = cpu;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (!init_pcfg(pcfg)) {
17162306a36Sopenharmony_ci		dev_err(dev, "portal init failed\n");
17262306a36Sopenharmony_ci		goto err_portal_init;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/* clear irq affinity if assigned cpu is offline */
17662306a36Sopenharmony_ci	if (!cpu_online(cpu))
17762306a36Sopenharmony_ci		bman_offline_cpu(cpu);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cicheck_cleanup:
18062306a36Sopenharmony_ci	if (__bman_portals_probed == 1 && bman_requires_cleanup()) {
18162306a36Sopenharmony_ci		/*
18262306a36Sopenharmony_ci		 * BMan wasn't reset prior to boot (Kexec for example)
18362306a36Sopenharmony_ci		 * Empty all the buffer pools so they are in reset state
18462306a36Sopenharmony_ci		 */
18562306a36Sopenharmony_ci		for (i = 0; i < BM_POOL_MAX; i++) {
18662306a36Sopenharmony_ci			err =  bm_shutdown_pool(i);
18762306a36Sopenharmony_ci			if (err) {
18862306a36Sopenharmony_ci				dev_err(dev, "Failed to shutdown bpool %d\n",
18962306a36Sopenharmony_ci					i);
19062306a36Sopenharmony_ci				goto err_portal_init;
19162306a36Sopenharmony_ci			}
19262306a36Sopenharmony_ci		}
19362306a36Sopenharmony_ci		bman_done_cleanup();
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	return 0;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cierr_portal_init:
19962306a36Sopenharmony_ci	iounmap(pcfg->addr_virt_ci);
20062306a36Sopenharmony_cierr_ioremap2:
20162306a36Sopenharmony_ci	memunmap(pcfg->addr_virt_ce);
20262306a36Sopenharmony_cierr_ioremap1:
20362306a36Sopenharmony_ci	 __bman_portals_probed = -1;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return -ENXIO;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic const struct of_device_id bman_portal_ids[] = {
20962306a36Sopenharmony_ci	{
21062306a36Sopenharmony_ci		.compatible = "fsl,bman-portal",
21162306a36Sopenharmony_ci	},
21262306a36Sopenharmony_ci	{}
21362306a36Sopenharmony_ci};
21462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, bman_portal_ids);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic struct platform_driver bman_portal_driver = {
21762306a36Sopenharmony_ci	.driver = {
21862306a36Sopenharmony_ci		.name = KBUILD_MODNAME,
21962306a36Sopenharmony_ci		.of_match_table = bman_portal_ids,
22062306a36Sopenharmony_ci	},
22162306a36Sopenharmony_ci	.probe = bman_portal_probe,
22262306a36Sopenharmony_ci};
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic int __init bman_portal_driver_register(struct platform_driver *drv)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	int ret;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	ret = platform_driver_register(drv);
22962306a36Sopenharmony_ci	if (ret < 0)
23062306a36Sopenharmony_ci		return ret;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
23362306a36Sopenharmony_ci					"soc/qbman_portal:online",
23462306a36Sopenharmony_ci					bman_online_cpu, bman_offline_cpu);
23562306a36Sopenharmony_ci	if (ret < 0) {
23662306a36Sopenharmony_ci		pr_err("bman: failed to register hotplug callbacks.\n");
23762306a36Sopenharmony_ci		platform_driver_unregister(drv);
23862306a36Sopenharmony_ci		return ret;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci	return 0;
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cimodule_driver(bman_portal_driver,
24462306a36Sopenharmony_ci	      bman_portal_driver_register, platform_driver_unregister);
245