162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2014-2016 Freescale Semiconductor Inc.
462306a36Sopenharmony_ci * Copyright NXP 2016
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/types.h>
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/io.h>
1662306a36Sopenharmony_ci#include <linux/sys_soc.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/fsl/mc.h>
1962306a36Sopenharmony_ci#include <soc/fsl/dpaa2-io.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include "qbman-portal.h"
2262306a36Sopenharmony_ci#include "dpio.h"
2362306a36Sopenharmony_ci#include "dpio-cmd.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
2662306a36Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor, Inc");
2762306a36Sopenharmony_ciMODULE_DESCRIPTION("DPIO Driver");
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct dpio_priv {
3062306a36Sopenharmony_ci	struct dpaa2_io *io;
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic cpumask_var_t cpus_unused_mask;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic const struct soc_device_attribute ls1088a_soc[] = {
3662306a36Sopenharmony_ci	{.family = "QorIQ LS1088A"},
3762306a36Sopenharmony_ci	{ /* sentinel */ }
3862306a36Sopenharmony_ci};
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic const struct soc_device_attribute ls2080a_soc[] = {
4162306a36Sopenharmony_ci	{.family = "QorIQ LS2080A"},
4262306a36Sopenharmony_ci	{ /* sentinel */ }
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic const struct soc_device_attribute ls2088a_soc[] = {
4662306a36Sopenharmony_ci	{.family = "QorIQ LS2088A"},
4762306a36Sopenharmony_ci	{ /* sentinel */ }
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic const struct soc_device_attribute lx2160a_soc[] = {
5162306a36Sopenharmony_ci	{.family = "QorIQ LX2160A"},
5262306a36Sopenharmony_ci	{ /* sentinel */ }
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic int dpaa2_dpio_get_cluster_sdest(struct fsl_mc_device *dpio_dev, int cpu)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	int cluster_base, cluster_size;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (soc_device_match(ls1088a_soc)) {
6062306a36Sopenharmony_ci		cluster_base = 2;
6162306a36Sopenharmony_ci		cluster_size = 4;
6262306a36Sopenharmony_ci	} else if (soc_device_match(ls2080a_soc) ||
6362306a36Sopenharmony_ci		   soc_device_match(ls2088a_soc) ||
6462306a36Sopenharmony_ci		   soc_device_match(lx2160a_soc)) {
6562306a36Sopenharmony_ci		cluster_base = 0;
6662306a36Sopenharmony_ci		cluster_size = 2;
6762306a36Sopenharmony_ci	} else {
6862306a36Sopenharmony_ci		dev_err(&dpio_dev->dev, "unknown SoC version\n");
6962306a36Sopenharmony_ci		return -1;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return cluster_base + cpu / cluster_size;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic irqreturn_t dpio_irq_handler(int irq_num, void *arg)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct device *dev = (struct device *)arg;
7862306a36Sopenharmony_ci	struct dpio_priv *priv = dev_get_drvdata(dev);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return dpaa2_io_irq(priv->io);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic void unregister_dpio_irq_handlers(struct fsl_mc_device *dpio_dev)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct fsl_mc_device_irq *irq;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	irq = dpio_dev->irqs[0];
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* clear the affinity hint */
9062306a36Sopenharmony_ci	irq_set_affinity_hint(irq->virq, NULL);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int register_dpio_irq_handlers(struct fsl_mc_device *dpio_dev, int cpu)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	int error;
9662306a36Sopenharmony_ci	struct fsl_mc_device_irq *irq;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	irq = dpio_dev->irqs[0];
9962306a36Sopenharmony_ci	error = devm_request_irq(&dpio_dev->dev,
10062306a36Sopenharmony_ci				 irq->virq,
10162306a36Sopenharmony_ci				 dpio_irq_handler,
10262306a36Sopenharmony_ci				 0,
10362306a36Sopenharmony_ci				 dev_name(&dpio_dev->dev),
10462306a36Sopenharmony_ci				 &dpio_dev->dev);
10562306a36Sopenharmony_ci	if (error < 0) {
10662306a36Sopenharmony_ci		dev_err(&dpio_dev->dev,
10762306a36Sopenharmony_ci			"devm_request_irq() failed: %d\n",
10862306a36Sopenharmony_ci			error);
10962306a36Sopenharmony_ci		return error;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	/* set the affinity hint */
11362306a36Sopenharmony_ci	if (irq_set_affinity_hint(irq->virq, cpumask_of(cpu)))
11462306a36Sopenharmony_ci		dev_err(&dpio_dev->dev,
11562306a36Sopenharmony_ci			"irq_set_affinity failed irq %d cpu %d\n",
11662306a36Sopenharmony_ci			irq->virq, cpu);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	return 0;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic int dpaa2_dpio_probe(struct fsl_mc_device *dpio_dev)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct dpio_attr dpio_attrs;
12462306a36Sopenharmony_ci	struct dpaa2_io_desc desc;
12562306a36Sopenharmony_ci	struct dpio_priv *priv;
12662306a36Sopenharmony_ci	int err = -ENOMEM;
12762306a36Sopenharmony_ci	struct device *dev = &dpio_dev->dev;
12862306a36Sopenharmony_ci	int possible_next_cpu;
12962306a36Sopenharmony_ci	int sdest;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
13262306a36Sopenharmony_ci	if (!priv)
13362306a36Sopenharmony_ci		goto err_priv_alloc;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	dev_set_drvdata(dev, priv);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	err = fsl_mc_portal_allocate(dpio_dev, 0, &dpio_dev->mc_io);
13862306a36Sopenharmony_ci	if (err) {
13962306a36Sopenharmony_ci		dev_dbg(dev, "MC portal allocation failed\n");
14062306a36Sopenharmony_ci		err = -EPROBE_DEFER;
14162306a36Sopenharmony_ci		goto err_priv_alloc;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	err = dpio_open(dpio_dev->mc_io, 0, dpio_dev->obj_desc.id,
14562306a36Sopenharmony_ci			&dpio_dev->mc_handle);
14662306a36Sopenharmony_ci	if (err) {
14762306a36Sopenharmony_ci		dev_err(dev, "dpio_open() failed\n");
14862306a36Sopenharmony_ci		goto err_open;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	err = dpio_reset(dpio_dev->mc_io, 0, dpio_dev->mc_handle);
15262306a36Sopenharmony_ci	if (err) {
15362306a36Sopenharmony_ci		dev_err(dev, "dpio_reset() failed\n");
15462306a36Sopenharmony_ci		goto err_reset;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	err = dpio_get_attributes(dpio_dev->mc_io, 0, dpio_dev->mc_handle,
15862306a36Sopenharmony_ci				  &dpio_attrs);
15962306a36Sopenharmony_ci	if (err) {
16062306a36Sopenharmony_ci		dev_err(dev, "dpio_get_attributes() failed %d\n", err);
16162306a36Sopenharmony_ci		goto err_get_attr;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci	desc.qman_version = dpio_attrs.qbman_version;
16462306a36Sopenharmony_ci	desc.qman_clk = dpio_attrs.clk;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	err = dpio_enable(dpio_dev->mc_io, 0, dpio_dev->mc_handle);
16762306a36Sopenharmony_ci	if (err) {
16862306a36Sopenharmony_ci		dev_err(dev, "dpio_enable() failed %d\n", err);
16962306a36Sopenharmony_ci		goto err_get_attr;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* initialize DPIO descriptor */
17362306a36Sopenharmony_ci	desc.receives_notifications = dpio_attrs.num_priorities ? 1 : 0;
17462306a36Sopenharmony_ci	desc.has_8prio = dpio_attrs.num_priorities == 8 ? 1 : 0;
17562306a36Sopenharmony_ci	desc.dpio_id = dpio_dev->obj_desc.id;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	/* get the cpu to use for the affinity hint */
17862306a36Sopenharmony_ci	possible_next_cpu = cpumask_first(cpus_unused_mask);
17962306a36Sopenharmony_ci	if (possible_next_cpu >= nr_cpu_ids) {
18062306a36Sopenharmony_ci		dev_err(dev, "probe failed. Number of DPIOs exceeds NR_CPUS.\n");
18162306a36Sopenharmony_ci		err = -ERANGE;
18262306a36Sopenharmony_ci		goto err_allocate_irqs;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci	desc.cpu = possible_next_cpu;
18562306a36Sopenharmony_ci	cpumask_clear_cpu(possible_next_cpu, cpus_unused_mask);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	sdest = dpaa2_dpio_get_cluster_sdest(dpio_dev, desc.cpu);
18862306a36Sopenharmony_ci	if (sdest >= 0) {
18962306a36Sopenharmony_ci		err = dpio_set_stashing_destination(dpio_dev->mc_io, 0,
19062306a36Sopenharmony_ci						    dpio_dev->mc_handle,
19162306a36Sopenharmony_ci						    sdest);
19262306a36Sopenharmony_ci		if (err)
19362306a36Sopenharmony_ci			dev_err(dev, "dpio_set_stashing_destination failed for cpu%d\n",
19462306a36Sopenharmony_ci				desc.cpu);
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (dpio_dev->obj_desc.region_count < 3) {
19862306a36Sopenharmony_ci		/* No support for DDR backed portals, use classic mapping */
19962306a36Sopenharmony_ci		/*
20062306a36Sopenharmony_ci		 * Set the CENA regs to be the cache inhibited area of the
20162306a36Sopenharmony_ci		 * portal to avoid coherency issues if a user migrates to
20262306a36Sopenharmony_ci		 * another core.
20362306a36Sopenharmony_ci		 */
20462306a36Sopenharmony_ci		desc.regs_cena = devm_memremap(dev, dpio_dev->regions[1].start,
20562306a36Sopenharmony_ci					resource_size(&dpio_dev->regions[1]),
20662306a36Sopenharmony_ci					MEMREMAP_WC);
20762306a36Sopenharmony_ci	} else {
20862306a36Sopenharmony_ci		desc.regs_cena = devm_memremap(dev, dpio_dev->regions[2].start,
20962306a36Sopenharmony_ci					resource_size(&dpio_dev->regions[2]),
21062306a36Sopenharmony_ci					MEMREMAP_WB);
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (IS_ERR(desc.regs_cena)) {
21462306a36Sopenharmony_ci		dev_err(dev, "devm_memremap failed\n");
21562306a36Sopenharmony_ci		err = PTR_ERR(desc.regs_cena);
21662306a36Sopenharmony_ci		goto err_allocate_irqs;
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	desc.regs_cinh = devm_ioremap(dev, dpio_dev->regions[1].start,
22062306a36Sopenharmony_ci				      resource_size(&dpio_dev->regions[1]));
22162306a36Sopenharmony_ci	if (!desc.regs_cinh) {
22262306a36Sopenharmony_ci		err = -ENOMEM;
22362306a36Sopenharmony_ci		dev_err(dev, "devm_ioremap failed\n");
22462306a36Sopenharmony_ci		goto err_allocate_irqs;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	err = fsl_mc_allocate_irqs(dpio_dev);
22862306a36Sopenharmony_ci	if (err) {
22962306a36Sopenharmony_ci		dev_err(dev, "fsl_mc_allocate_irqs failed. err=%d\n", err);
23062306a36Sopenharmony_ci		goto err_allocate_irqs;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	priv->io = dpaa2_io_create(&desc, dev);
23462306a36Sopenharmony_ci	if (!priv->io) {
23562306a36Sopenharmony_ci		dev_err(dev, "dpaa2_io_create failed\n");
23662306a36Sopenharmony_ci		err = -ENOMEM;
23762306a36Sopenharmony_ci		goto err_dpaa2_io_create;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	err = register_dpio_irq_handlers(dpio_dev, desc.cpu);
24162306a36Sopenharmony_ci	if (err)
24262306a36Sopenharmony_ci		goto err_register_dpio_irq;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	dev_info(dev, "probed\n");
24562306a36Sopenharmony_ci	dev_dbg(dev, "   receives_notifications = %d\n",
24662306a36Sopenharmony_ci		desc.receives_notifications);
24762306a36Sopenharmony_ci	dpio_close(dpio_dev->mc_io, 0, dpio_dev->mc_handle);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	return 0;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cierr_dpaa2_io_create:
25262306a36Sopenharmony_ci	unregister_dpio_irq_handlers(dpio_dev);
25362306a36Sopenharmony_cierr_register_dpio_irq:
25462306a36Sopenharmony_ci	fsl_mc_free_irqs(dpio_dev);
25562306a36Sopenharmony_cierr_allocate_irqs:
25662306a36Sopenharmony_ci	dpio_disable(dpio_dev->mc_io, 0, dpio_dev->mc_handle);
25762306a36Sopenharmony_cierr_get_attr:
25862306a36Sopenharmony_cierr_reset:
25962306a36Sopenharmony_ci	dpio_close(dpio_dev->mc_io, 0, dpio_dev->mc_handle);
26062306a36Sopenharmony_cierr_open:
26162306a36Sopenharmony_ci	fsl_mc_portal_free(dpio_dev->mc_io);
26262306a36Sopenharmony_cierr_priv_alloc:
26362306a36Sopenharmony_ci	return err;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci/* Tear down interrupts for a given DPIO object */
26762306a36Sopenharmony_cistatic void dpio_teardown_irqs(struct fsl_mc_device *dpio_dev)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	unregister_dpio_irq_handlers(dpio_dev);
27062306a36Sopenharmony_ci	fsl_mc_free_irqs(dpio_dev);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic void dpaa2_dpio_remove(struct fsl_mc_device *dpio_dev)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct device *dev;
27662306a36Sopenharmony_ci	struct dpio_priv *priv;
27762306a36Sopenharmony_ci	int err = 0, cpu;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	dev = &dpio_dev->dev;
28062306a36Sopenharmony_ci	priv = dev_get_drvdata(dev);
28162306a36Sopenharmony_ci	cpu = dpaa2_io_get_cpu(priv->io);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	dpaa2_io_down(priv->io);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	dpio_teardown_irqs(dpio_dev);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	cpumask_set_cpu(cpu, cpus_unused_mask);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	err = dpio_open(dpio_dev->mc_io, 0, dpio_dev->obj_desc.id,
29062306a36Sopenharmony_ci			&dpio_dev->mc_handle);
29162306a36Sopenharmony_ci	if (err) {
29262306a36Sopenharmony_ci		dev_err(dev, "dpio_open() failed\n");
29362306a36Sopenharmony_ci		goto err_open;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	dpio_disable(dpio_dev->mc_io, 0, dpio_dev->mc_handle);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	dpio_close(dpio_dev->mc_io, 0, dpio_dev->mc_handle);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cierr_open:
30162306a36Sopenharmony_ci	fsl_mc_portal_free(dpio_dev->mc_io);
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic const struct fsl_mc_device_id dpaa2_dpio_match_id_table[] = {
30562306a36Sopenharmony_ci	{
30662306a36Sopenharmony_ci		.vendor = FSL_MC_VENDOR_FREESCALE,
30762306a36Sopenharmony_ci		.obj_type = "dpio",
30862306a36Sopenharmony_ci	},
30962306a36Sopenharmony_ci	{ .vendor = 0x0 }
31062306a36Sopenharmony_ci};
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic struct fsl_mc_driver dpaa2_dpio_driver = {
31362306a36Sopenharmony_ci	.driver = {
31462306a36Sopenharmony_ci		.name		= KBUILD_MODNAME,
31562306a36Sopenharmony_ci		.owner		= THIS_MODULE,
31662306a36Sopenharmony_ci	},
31762306a36Sopenharmony_ci	.probe		= dpaa2_dpio_probe,
31862306a36Sopenharmony_ci	.remove		= dpaa2_dpio_remove,
31962306a36Sopenharmony_ci	.match_id_table = dpaa2_dpio_match_id_table
32062306a36Sopenharmony_ci};
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic int dpio_driver_init(void)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	if (!zalloc_cpumask_var(&cpus_unused_mask, GFP_KERNEL))
32562306a36Sopenharmony_ci		return -ENOMEM;
32662306a36Sopenharmony_ci	cpumask_copy(cpus_unused_mask, cpu_online_mask);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	return fsl_mc_driver_register(&dpaa2_dpio_driver);
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic void dpio_driver_exit(void)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	free_cpumask_var(cpus_unused_mask);
33462306a36Sopenharmony_ci	fsl_mc_driver_unregister(&dpaa2_dpio_driver);
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_cimodule_init(dpio_driver_init);
33762306a36Sopenharmony_cimodule_exit(dpio_driver_exit);
338