162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2011 Freescale Semiconductor, Inc
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Freescale Integrated Flash Controller
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Dipen Dudhat <Dipen.Dudhat@freescale.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/compiler.h>
1262306a36Sopenharmony_ci#include <linux/sched.h>
1362306a36Sopenharmony_ci#include <linux/spinlock.h>
1462306a36Sopenharmony_ci#include <linux/types.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/io.h>
1762306a36Sopenharmony_ci#include <linux/of.h>
1862306a36Sopenharmony_ci#include <linux/of_platform.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <linux/fsl_ifc.h>
2162306a36Sopenharmony_ci#include <linux/irqdomain.h>
2262306a36Sopenharmony_ci#include <linux/of_address.h>
2362306a36Sopenharmony_ci#include <linux/of_irq.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistruct fsl_ifc_ctrl *fsl_ifc_ctrl_dev;
2662306a36Sopenharmony_ciEXPORT_SYMBOL(fsl_ifc_ctrl_dev);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/*
2962306a36Sopenharmony_ci * convert_ifc_address - convert the base address
3062306a36Sopenharmony_ci * @addr_base:	base address of the memory bank
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ciunsigned int convert_ifc_address(phys_addr_t addr_base)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	return addr_base & CSPR_BA;
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ciEXPORT_SYMBOL(convert_ifc_address);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/*
3962306a36Sopenharmony_ci * fsl_ifc_find - find IFC bank
4062306a36Sopenharmony_ci * @addr_base:	base address of the memory bank
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci * This function walks IFC banks comparing "Base address" field of the CSPR
4362306a36Sopenharmony_ci * registers with the supplied addr_base argument. When bases match this
4462306a36Sopenharmony_ci * function returns bank number (starting with 0), otherwise it returns
4562306a36Sopenharmony_ci * appropriate errno value.
4662306a36Sopenharmony_ci */
4762306a36Sopenharmony_ciint fsl_ifc_find(phys_addr_t addr_base)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	int i = 0;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->gregs)
5262306a36Sopenharmony_ci		return -ENODEV;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	for (i = 0; i < fsl_ifc_ctrl_dev->banks; i++) {
5562306a36Sopenharmony_ci		u32 cspr = ifc_in32(&fsl_ifc_ctrl_dev->gregs->cspr_cs[i].cspr);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci		if (cspr & CSPR_V && (cspr & CSPR_BA) ==
5862306a36Sopenharmony_ci				convert_ifc_address(addr_base))
5962306a36Sopenharmony_ci			return i;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return -ENOENT;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ciEXPORT_SYMBOL(fsl_ifc_find);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic int fsl_ifc_ctrl_init(struct fsl_ifc_ctrl *ctrl)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct fsl_ifc_global __iomem *ifc = ctrl->gregs;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/*
7162306a36Sopenharmony_ci	 * Clear all the common status and event registers
7262306a36Sopenharmony_ci	 */
7362306a36Sopenharmony_ci	if (ifc_in32(&ifc->cm_evter_stat) & IFC_CM_EVTER_STAT_CSER)
7462306a36Sopenharmony_ci		ifc_out32(IFC_CM_EVTER_STAT_CSER, &ifc->cm_evter_stat);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/* enable all error and events */
7762306a36Sopenharmony_ci	ifc_out32(IFC_CM_EVTER_EN_CSEREN, &ifc->cm_evter_en);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/* enable all error and event interrupts */
8062306a36Sopenharmony_ci	ifc_out32(IFC_CM_EVTER_INTR_EN_CSERIREN, &ifc->cm_evter_intr_en);
8162306a36Sopenharmony_ci	ifc_out32(0x0, &ifc->cm_erattr0);
8262306a36Sopenharmony_ci	ifc_out32(0x0, &ifc->cm_erattr1);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return 0;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int fsl_ifc_ctrl_remove(struct platform_device *dev)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct fsl_ifc_ctrl *ctrl = dev_get_drvdata(&dev->dev);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	of_platform_depopulate(&dev->dev);
9262306a36Sopenharmony_ci	free_irq(ctrl->nand_irq, ctrl);
9362306a36Sopenharmony_ci	free_irq(ctrl->irq, ctrl);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	irq_dispose_mapping(ctrl->nand_irq);
9662306a36Sopenharmony_ci	irq_dispose_mapping(ctrl->irq);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	iounmap(ctrl->gregs);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	dev_set_drvdata(&dev->dev, NULL);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return 0;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/*
10662306a36Sopenharmony_ci * NAND events are split between an operational interrupt which only
10762306a36Sopenharmony_ci * receives OPC, and an error interrupt that receives everything else,
10862306a36Sopenharmony_ci * including non-NAND errors.  Whichever interrupt gets to it first
10962306a36Sopenharmony_ci * records the status and wakes the wait queue.
11062306a36Sopenharmony_ci */
11162306a36Sopenharmony_cistatic DEFINE_SPINLOCK(nand_irq_lock);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic u32 check_nand_stat(struct fsl_ifc_ctrl *ctrl)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
11662306a36Sopenharmony_ci	unsigned long flags;
11762306a36Sopenharmony_ci	u32 stat;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	spin_lock_irqsave(&nand_irq_lock, flags);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	stat = ifc_in32(&ifc->ifc_nand.nand_evter_stat);
12262306a36Sopenharmony_ci	if (stat) {
12362306a36Sopenharmony_ci		ifc_out32(stat, &ifc->ifc_nand.nand_evter_stat);
12462306a36Sopenharmony_ci		ctrl->nand_stat = stat;
12562306a36Sopenharmony_ci		wake_up(&ctrl->nand_wait);
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	spin_unlock_irqrestore(&nand_irq_lock, flags);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return stat;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic irqreturn_t fsl_ifc_nand_irq(int irqno, void *data)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct fsl_ifc_ctrl *ctrl = data;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (check_nand_stat(ctrl))
13862306a36Sopenharmony_ci		return IRQ_HANDLED;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	return IRQ_NONE;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/*
14462306a36Sopenharmony_ci * NOTE: This interrupt is used to report ifc events of various kinds,
14562306a36Sopenharmony_ci * such as transaction errors on the chipselects.
14662306a36Sopenharmony_ci */
14762306a36Sopenharmony_cistatic irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	struct fsl_ifc_ctrl *ctrl = data;
15062306a36Sopenharmony_ci	struct fsl_ifc_global __iomem *ifc = ctrl->gregs;
15162306a36Sopenharmony_ci	u32 err_axiid, err_srcid, status, cs_err, err_addr;
15262306a36Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	/* read for chip select error */
15562306a36Sopenharmony_ci	cs_err = ifc_in32(&ifc->cm_evter_stat);
15662306a36Sopenharmony_ci	if (cs_err) {
15762306a36Sopenharmony_ci		dev_err(ctrl->dev, "transaction sent to IFC is not mapped to any memory bank 0x%08X\n",
15862306a36Sopenharmony_ci			cs_err);
15962306a36Sopenharmony_ci		/* clear the chip select error */
16062306a36Sopenharmony_ci		ifc_out32(IFC_CM_EVTER_STAT_CSER, &ifc->cm_evter_stat);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci		/* read error attribute registers print the error information */
16362306a36Sopenharmony_ci		status = ifc_in32(&ifc->cm_erattr0);
16462306a36Sopenharmony_ci		err_addr = ifc_in32(&ifc->cm_erattr1);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci		if (status & IFC_CM_ERATTR0_ERTYP_READ)
16762306a36Sopenharmony_ci			dev_err(ctrl->dev, "Read transaction error CM_ERATTR0 0x%08X\n",
16862306a36Sopenharmony_ci				status);
16962306a36Sopenharmony_ci		else
17062306a36Sopenharmony_ci			dev_err(ctrl->dev, "Write transaction error CM_ERATTR0 0x%08X\n",
17162306a36Sopenharmony_ci				status);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci		err_axiid = (status & IFC_CM_ERATTR0_ERAID) >>
17462306a36Sopenharmony_ci					IFC_CM_ERATTR0_ERAID_SHIFT;
17562306a36Sopenharmony_ci		dev_err(ctrl->dev, "AXI ID of the error transaction 0x%08X\n",
17662306a36Sopenharmony_ci			err_axiid);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci		err_srcid = (status & IFC_CM_ERATTR0_ESRCID) >>
17962306a36Sopenharmony_ci					IFC_CM_ERATTR0_ESRCID_SHIFT;
18062306a36Sopenharmony_ci		dev_err(ctrl->dev, "SRC ID of the error transaction 0x%08X\n",
18162306a36Sopenharmony_ci			err_srcid);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci		dev_err(ctrl->dev, "Transaction Address corresponding to error ERADDR 0x%08X\n",
18462306a36Sopenharmony_ci			err_addr);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci		ret = IRQ_HANDLED;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (check_nand_stat(ctrl))
19062306a36Sopenharmony_ci		ret = IRQ_HANDLED;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return ret;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci/*
19662306a36Sopenharmony_ci * fsl_ifc_ctrl_probe
19762306a36Sopenharmony_ci *
19862306a36Sopenharmony_ci * called by device layer when it finds a device matching
19962306a36Sopenharmony_ci * one our driver can handled. This code allocates all of
20062306a36Sopenharmony_ci * the resources needed for the controller only.  The
20162306a36Sopenharmony_ci * resources for the NAND banks themselves are allocated
20262306a36Sopenharmony_ci * in the chip probe function.
20362306a36Sopenharmony_ci */
20462306a36Sopenharmony_cistatic int fsl_ifc_ctrl_probe(struct platform_device *dev)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	int ret = 0;
20762306a36Sopenharmony_ci	int version, banks;
20862306a36Sopenharmony_ci	void __iomem *addr;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	dev_info(&dev->dev, "Freescale Integrated Flash Controller\n");
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	fsl_ifc_ctrl_dev = devm_kzalloc(&dev->dev, sizeof(*fsl_ifc_ctrl_dev),
21362306a36Sopenharmony_ci					GFP_KERNEL);
21462306a36Sopenharmony_ci	if (!fsl_ifc_ctrl_dev)
21562306a36Sopenharmony_ci		return -ENOMEM;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	dev_set_drvdata(&dev->dev, fsl_ifc_ctrl_dev);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	/* IOMAP the entire IFC region */
22062306a36Sopenharmony_ci	fsl_ifc_ctrl_dev->gregs = of_iomap(dev->dev.of_node, 0);
22162306a36Sopenharmony_ci	if (!fsl_ifc_ctrl_dev->gregs) {
22262306a36Sopenharmony_ci		dev_err(&dev->dev, "failed to get memory region\n");
22362306a36Sopenharmony_ci		return -ENODEV;
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (of_property_read_bool(dev->dev.of_node, "little-endian")) {
22762306a36Sopenharmony_ci		fsl_ifc_ctrl_dev->little_endian = true;
22862306a36Sopenharmony_ci		dev_dbg(&dev->dev, "IFC REGISTERS are LITTLE endian\n");
22962306a36Sopenharmony_ci	} else {
23062306a36Sopenharmony_ci		fsl_ifc_ctrl_dev->little_endian = false;
23162306a36Sopenharmony_ci		dev_dbg(&dev->dev, "IFC REGISTERS are BIG endian\n");
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	version = ifc_in32(&fsl_ifc_ctrl_dev->gregs->ifc_rev) &
23562306a36Sopenharmony_ci			FSL_IFC_VERSION_MASK;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8;
23862306a36Sopenharmony_ci	dev_info(&dev->dev, "IFC version %d.%d, %d banks\n",
23962306a36Sopenharmony_ci		version >> 24, (version >> 16) & 0xf, banks);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	fsl_ifc_ctrl_dev->version = version;
24262306a36Sopenharmony_ci	fsl_ifc_ctrl_dev->banks = banks;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	addr = fsl_ifc_ctrl_dev->gregs;
24562306a36Sopenharmony_ci	if (version >= FSL_IFC_VERSION_2_0_0)
24662306a36Sopenharmony_ci		addr += PGOFFSET_64K;
24762306a36Sopenharmony_ci	else
24862306a36Sopenharmony_ci		addr += PGOFFSET_4K;
24962306a36Sopenharmony_ci	fsl_ifc_ctrl_dev->rregs = addr;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	/* get the Controller level irq */
25262306a36Sopenharmony_ci	fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0);
25362306a36Sopenharmony_ci	if (fsl_ifc_ctrl_dev->irq == 0) {
25462306a36Sopenharmony_ci		dev_err(&dev->dev, "failed to get irq resource for IFC\n");
25562306a36Sopenharmony_ci		ret = -ENODEV;
25662306a36Sopenharmony_ci		goto err;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* get the nand machine irq */
26062306a36Sopenharmony_ci	fsl_ifc_ctrl_dev->nand_irq =
26162306a36Sopenharmony_ci			irq_of_parse_and_map(dev->dev.of_node, 1);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	fsl_ifc_ctrl_dev->dev = &dev->dev;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	ret = fsl_ifc_ctrl_init(fsl_ifc_ctrl_dev);
26662306a36Sopenharmony_ci	if (ret < 0)
26762306a36Sopenharmony_ci		goto err_unmap_nandirq;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	init_waitqueue_head(&fsl_ifc_ctrl_dev->nand_wait);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	ret = request_irq(fsl_ifc_ctrl_dev->irq, fsl_ifc_ctrl_irq, IRQF_SHARED,
27262306a36Sopenharmony_ci			  "fsl-ifc", fsl_ifc_ctrl_dev);
27362306a36Sopenharmony_ci	if (ret != 0) {
27462306a36Sopenharmony_ci		dev_err(&dev->dev, "failed to install irq (%d)\n",
27562306a36Sopenharmony_ci			fsl_ifc_ctrl_dev->irq);
27662306a36Sopenharmony_ci		goto err_unmap_nandirq;
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (fsl_ifc_ctrl_dev->nand_irq) {
28062306a36Sopenharmony_ci		ret = request_irq(fsl_ifc_ctrl_dev->nand_irq, fsl_ifc_nand_irq,
28162306a36Sopenharmony_ci				0, "fsl-ifc-nand", fsl_ifc_ctrl_dev);
28262306a36Sopenharmony_ci		if (ret != 0) {
28362306a36Sopenharmony_ci			dev_err(&dev->dev, "failed to install irq (%d)\n",
28462306a36Sopenharmony_ci				fsl_ifc_ctrl_dev->nand_irq);
28562306a36Sopenharmony_ci			goto err_free_irq;
28662306a36Sopenharmony_ci		}
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/* legacy dts may still use "simple-bus" compatible */
29062306a36Sopenharmony_ci	ret = of_platform_default_populate(dev->dev.of_node, NULL, &dev->dev);
29162306a36Sopenharmony_ci	if (ret)
29262306a36Sopenharmony_ci		goto err_free_nandirq;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	return 0;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cierr_free_nandirq:
29762306a36Sopenharmony_ci	free_irq(fsl_ifc_ctrl_dev->nand_irq, fsl_ifc_ctrl_dev);
29862306a36Sopenharmony_cierr_free_irq:
29962306a36Sopenharmony_ci	free_irq(fsl_ifc_ctrl_dev->irq, fsl_ifc_ctrl_dev);
30062306a36Sopenharmony_cierr_unmap_nandirq:
30162306a36Sopenharmony_ci	irq_dispose_mapping(fsl_ifc_ctrl_dev->nand_irq);
30262306a36Sopenharmony_ci	irq_dispose_mapping(fsl_ifc_ctrl_dev->irq);
30362306a36Sopenharmony_cierr:
30462306a36Sopenharmony_ci	iounmap(fsl_ifc_ctrl_dev->gregs);
30562306a36Sopenharmony_ci	return ret;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic const struct of_device_id fsl_ifc_match[] = {
30962306a36Sopenharmony_ci	{
31062306a36Sopenharmony_ci		.compatible = "fsl,ifc",
31162306a36Sopenharmony_ci	},
31262306a36Sopenharmony_ci	{},
31362306a36Sopenharmony_ci};
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic struct platform_driver fsl_ifc_ctrl_driver = {
31662306a36Sopenharmony_ci	.driver = {
31762306a36Sopenharmony_ci		.name	= "fsl-ifc",
31862306a36Sopenharmony_ci		.of_match_table = fsl_ifc_match,
31962306a36Sopenharmony_ci	},
32062306a36Sopenharmony_ci	.probe       = fsl_ifc_ctrl_probe,
32162306a36Sopenharmony_ci	.remove      = fsl_ifc_ctrl_remove,
32262306a36Sopenharmony_ci};
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic int __init fsl_ifc_init(void)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	return platform_driver_register(&fsl_ifc_ctrl_driver);
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_cisubsys_initcall(fsl_ifc_init);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor");
33162306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale Integrated Flash Controller driver");
332