18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2011 Freescale Semiconductor, Inc
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Freescale Integrated Flash Controller
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Dipen Dudhat <Dipen.Dudhat@freescale.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/compiler.h>
128c2ecf20Sopenharmony_ci#include <linux/sched.h>
138c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
148c2ecf20Sopenharmony_ci#include <linux/types.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/io.h>
178c2ecf20Sopenharmony_ci#include <linux/of.h>
188c2ecf20Sopenharmony_ci#include <linux/of_device.h>
198c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
208c2ecf20Sopenharmony_ci#include <linux/fsl_ifc.h>
218c2ecf20Sopenharmony_ci#include <linux/irqdomain.h>
228c2ecf20Sopenharmony_ci#include <linux/of_address.h>
238c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistruct fsl_ifc_ctrl *fsl_ifc_ctrl_dev;
268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fsl_ifc_ctrl_dev);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/*
298c2ecf20Sopenharmony_ci * convert_ifc_address - convert the base address
308c2ecf20Sopenharmony_ci * @addr_base:	base address of the memory bank
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ciunsigned int convert_ifc_address(phys_addr_t addr_base)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	return addr_base & CSPR_BA;
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(convert_ifc_address);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/*
398c2ecf20Sopenharmony_ci * fsl_ifc_find - find IFC bank
408c2ecf20Sopenharmony_ci * @addr_base:	base address of the memory bank
418c2ecf20Sopenharmony_ci *
428c2ecf20Sopenharmony_ci * This function walks IFC banks comparing "Base address" field of the CSPR
438c2ecf20Sopenharmony_ci * registers with the supplied addr_base argument. When bases match this
448c2ecf20Sopenharmony_ci * function returns bank number (starting with 0), otherwise it returns
458c2ecf20Sopenharmony_ci * appropriate errno value.
468c2ecf20Sopenharmony_ci */
478c2ecf20Sopenharmony_ciint fsl_ifc_find(phys_addr_t addr_base)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	int i = 0;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->gregs)
528c2ecf20Sopenharmony_ci		return -ENODEV;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	for (i = 0; i < fsl_ifc_ctrl_dev->banks; i++) {
558c2ecf20Sopenharmony_ci		u32 cspr = ifc_in32(&fsl_ifc_ctrl_dev->gregs->cspr_cs[i].cspr);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci		if (cspr & CSPR_V && (cspr & CSPR_BA) ==
588c2ecf20Sopenharmony_ci				convert_ifc_address(addr_base))
598c2ecf20Sopenharmony_ci			return i;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	return -ENOENT;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fsl_ifc_find);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic int fsl_ifc_ctrl_init(struct fsl_ifc_ctrl *ctrl)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct fsl_ifc_global __iomem *ifc = ctrl->gregs;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	/*
718c2ecf20Sopenharmony_ci	 * Clear all the common status and event registers
728c2ecf20Sopenharmony_ci	 */
738c2ecf20Sopenharmony_ci	if (ifc_in32(&ifc->cm_evter_stat) & IFC_CM_EVTER_STAT_CSER)
748c2ecf20Sopenharmony_ci		ifc_out32(IFC_CM_EVTER_STAT_CSER, &ifc->cm_evter_stat);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	/* enable all error and events */
778c2ecf20Sopenharmony_ci	ifc_out32(IFC_CM_EVTER_EN_CSEREN, &ifc->cm_evter_en);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	/* enable all error and event interrupts */
808c2ecf20Sopenharmony_ci	ifc_out32(IFC_CM_EVTER_INTR_EN_CSERIREN, &ifc->cm_evter_intr_en);
818c2ecf20Sopenharmony_ci	ifc_out32(0x0, &ifc->cm_erattr0);
828c2ecf20Sopenharmony_ci	ifc_out32(0x0, &ifc->cm_erattr1);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return 0;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int fsl_ifc_ctrl_remove(struct platform_device *dev)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	struct fsl_ifc_ctrl *ctrl = dev_get_drvdata(&dev->dev);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	free_irq(ctrl->nand_irq, ctrl);
928c2ecf20Sopenharmony_ci	free_irq(ctrl->irq, ctrl);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	irq_dispose_mapping(ctrl->nand_irq);
958c2ecf20Sopenharmony_ci	irq_dispose_mapping(ctrl->irq);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	iounmap(ctrl->gregs);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	dev_set_drvdata(&dev->dev, NULL);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return 0;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/*
1058c2ecf20Sopenharmony_ci * NAND events are split between an operational interrupt which only
1068c2ecf20Sopenharmony_ci * receives OPC, and an error interrupt that receives everything else,
1078c2ecf20Sopenharmony_ci * including non-NAND errors.  Whichever interrupt gets to it first
1088c2ecf20Sopenharmony_ci * records the status and wakes the wait queue.
1098c2ecf20Sopenharmony_ci */
1108c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(nand_irq_lock);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic u32 check_nand_stat(struct fsl_ifc_ctrl *ctrl)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs;
1158c2ecf20Sopenharmony_ci	unsigned long flags;
1168c2ecf20Sopenharmony_ci	u32 stat;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	spin_lock_irqsave(&nand_irq_lock, flags);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	stat = ifc_in32(&ifc->ifc_nand.nand_evter_stat);
1218c2ecf20Sopenharmony_ci	if (stat) {
1228c2ecf20Sopenharmony_ci		ifc_out32(stat, &ifc->ifc_nand.nand_evter_stat);
1238c2ecf20Sopenharmony_ci		ctrl->nand_stat = stat;
1248c2ecf20Sopenharmony_ci		wake_up(&ctrl->nand_wait);
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&nand_irq_lock, flags);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	return stat;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic irqreturn_t fsl_ifc_nand_irq(int irqno, void *data)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	struct fsl_ifc_ctrl *ctrl = data;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (check_nand_stat(ctrl))
1378c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	return IRQ_NONE;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci/*
1438c2ecf20Sopenharmony_ci * NOTE: This interrupt is used to report ifc events of various kinds,
1448c2ecf20Sopenharmony_ci * such as transaction errors on the chipselects.
1458c2ecf20Sopenharmony_ci */
1468c2ecf20Sopenharmony_cistatic irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	struct fsl_ifc_ctrl *ctrl = data;
1498c2ecf20Sopenharmony_ci	struct fsl_ifc_global __iomem *ifc = ctrl->gregs;
1508c2ecf20Sopenharmony_ci	u32 err_axiid, err_srcid, status, cs_err, err_addr;
1518c2ecf20Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	/* read for chip select error */
1548c2ecf20Sopenharmony_ci	cs_err = ifc_in32(&ifc->cm_evter_stat);
1558c2ecf20Sopenharmony_ci	if (cs_err) {
1568c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "transaction sent to IFC is not mapped to any memory bank 0x%08X\n",
1578c2ecf20Sopenharmony_ci			cs_err);
1588c2ecf20Sopenharmony_ci		/* clear the chip select error */
1598c2ecf20Sopenharmony_ci		ifc_out32(IFC_CM_EVTER_STAT_CSER, &ifc->cm_evter_stat);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		/* read error attribute registers print the error information */
1628c2ecf20Sopenharmony_ci		status = ifc_in32(&ifc->cm_erattr0);
1638c2ecf20Sopenharmony_ci		err_addr = ifc_in32(&ifc->cm_erattr1);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		if (status & IFC_CM_ERATTR0_ERTYP_READ)
1668c2ecf20Sopenharmony_ci			dev_err(ctrl->dev, "Read transaction error CM_ERATTR0 0x%08X\n",
1678c2ecf20Sopenharmony_ci				status);
1688c2ecf20Sopenharmony_ci		else
1698c2ecf20Sopenharmony_ci			dev_err(ctrl->dev, "Write transaction error CM_ERATTR0 0x%08X\n",
1708c2ecf20Sopenharmony_ci				status);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci		err_axiid = (status & IFC_CM_ERATTR0_ERAID) >>
1738c2ecf20Sopenharmony_ci					IFC_CM_ERATTR0_ERAID_SHIFT;
1748c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "AXI ID of the error transaction 0x%08X\n",
1758c2ecf20Sopenharmony_ci			err_axiid);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci		err_srcid = (status & IFC_CM_ERATTR0_ESRCID) >>
1788c2ecf20Sopenharmony_ci					IFC_CM_ERATTR0_ESRCID_SHIFT;
1798c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "SRC ID of the error transaction 0x%08X\n",
1808c2ecf20Sopenharmony_ci			err_srcid);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "Transaction Address corresponding to error ERADDR 0x%08X\n",
1838c2ecf20Sopenharmony_ci			err_addr);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (check_nand_stat(ctrl))
1898c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return ret;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci/*
1958c2ecf20Sopenharmony_ci * fsl_ifc_ctrl_probe
1968c2ecf20Sopenharmony_ci *
1978c2ecf20Sopenharmony_ci * called by device layer when it finds a device matching
1988c2ecf20Sopenharmony_ci * one our driver can handled. This code allocates all of
1998c2ecf20Sopenharmony_ci * the resources needed for the controller only.  The
2008c2ecf20Sopenharmony_ci * resources for the NAND banks themselves are allocated
2018c2ecf20Sopenharmony_ci * in the chip probe function.
2028c2ecf20Sopenharmony_ci */
2038c2ecf20Sopenharmony_cistatic int fsl_ifc_ctrl_probe(struct platform_device *dev)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	int ret = 0;
2068c2ecf20Sopenharmony_ci	int version, banks;
2078c2ecf20Sopenharmony_ci	void __iomem *addr;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	dev_info(&dev->dev, "Freescale Integrated Flash Controller\n");
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	fsl_ifc_ctrl_dev = devm_kzalloc(&dev->dev, sizeof(*fsl_ifc_ctrl_dev),
2128c2ecf20Sopenharmony_ci					GFP_KERNEL);
2138c2ecf20Sopenharmony_ci	if (!fsl_ifc_ctrl_dev)
2148c2ecf20Sopenharmony_ci		return -ENOMEM;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	dev_set_drvdata(&dev->dev, fsl_ifc_ctrl_dev);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	/* IOMAP the entire IFC region */
2198c2ecf20Sopenharmony_ci	fsl_ifc_ctrl_dev->gregs = of_iomap(dev->dev.of_node, 0);
2208c2ecf20Sopenharmony_ci	if (!fsl_ifc_ctrl_dev->gregs) {
2218c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "failed to get memory region\n");
2228c2ecf20Sopenharmony_ci		return -ENODEV;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	if (of_property_read_bool(dev->dev.of_node, "little-endian")) {
2268c2ecf20Sopenharmony_ci		fsl_ifc_ctrl_dev->little_endian = true;
2278c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev, "IFC REGISTERS are LITTLE endian\n");
2288c2ecf20Sopenharmony_ci	} else {
2298c2ecf20Sopenharmony_ci		fsl_ifc_ctrl_dev->little_endian = false;
2308c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev, "IFC REGISTERS are BIG endian\n");
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	version = ifc_in32(&fsl_ifc_ctrl_dev->gregs->ifc_rev) &
2348c2ecf20Sopenharmony_ci			FSL_IFC_VERSION_MASK;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8;
2378c2ecf20Sopenharmony_ci	dev_info(&dev->dev, "IFC version %d.%d, %d banks\n",
2388c2ecf20Sopenharmony_ci		version >> 24, (version >> 16) & 0xf, banks);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	fsl_ifc_ctrl_dev->version = version;
2418c2ecf20Sopenharmony_ci	fsl_ifc_ctrl_dev->banks = banks;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	addr = fsl_ifc_ctrl_dev->gregs;
2448c2ecf20Sopenharmony_ci	if (version >= FSL_IFC_VERSION_2_0_0)
2458c2ecf20Sopenharmony_ci		addr += PGOFFSET_64K;
2468c2ecf20Sopenharmony_ci	else
2478c2ecf20Sopenharmony_ci		addr += PGOFFSET_4K;
2488c2ecf20Sopenharmony_ci	fsl_ifc_ctrl_dev->rregs = addr;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	/* get the Controller level irq */
2518c2ecf20Sopenharmony_ci	fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0);
2528c2ecf20Sopenharmony_ci	if (fsl_ifc_ctrl_dev->irq == 0) {
2538c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "failed to get irq resource for IFC\n");
2548c2ecf20Sopenharmony_ci		ret = -ENODEV;
2558c2ecf20Sopenharmony_ci		goto err;
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	/* get the nand machine irq */
2598c2ecf20Sopenharmony_ci	fsl_ifc_ctrl_dev->nand_irq =
2608c2ecf20Sopenharmony_ci			irq_of_parse_and_map(dev->dev.of_node, 1);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	fsl_ifc_ctrl_dev->dev = &dev->dev;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	ret = fsl_ifc_ctrl_init(fsl_ifc_ctrl_dev);
2658c2ecf20Sopenharmony_ci	if (ret < 0)
2668c2ecf20Sopenharmony_ci		goto err_unmap_nandirq;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	init_waitqueue_head(&fsl_ifc_ctrl_dev->nand_wait);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	ret = request_irq(fsl_ifc_ctrl_dev->irq, fsl_ifc_ctrl_irq, IRQF_SHARED,
2718c2ecf20Sopenharmony_ci			  "fsl-ifc", fsl_ifc_ctrl_dev);
2728c2ecf20Sopenharmony_ci	if (ret != 0) {
2738c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "failed to install irq (%d)\n",
2748c2ecf20Sopenharmony_ci			fsl_ifc_ctrl_dev->irq);
2758c2ecf20Sopenharmony_ci		goto err_unmap_nandirq;
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if (fsl_ifc_ctrl_dev->nand_irq) {
2798c2ecf20Sopenharmony_ci		ret = request_irq(fsl_ifc_ctrl_dev->nand_irq, fsl_ifc_nand_irq,
2808c2ecf20Sopenharmony_ci				0, "fsl-ifc-nand", fsl_ifc_ctrl_dev);
2818c2ecf20Sopenharmony_ci		if (ret != 0) {
2828c2ecf20Sopenharmony_ci			dev_err(&dev->dev, "failed to install irq (%d)\n",
2838c2ecf20Sopenharmony_ci				fsl_ifc_ctrl_dev->nand_irq);
2848c2ecf20Sopenharmony_ci			goto err_free_irq;
2858c2ecf20Sopenharmony_ci		}
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	return 0;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cierr_free_irq:
2918c2ecf20Sopenharmony_ci	free_irq(fsl_ifc_ctrl_dev->irq, fsl_ifc_ctrl_dev);
2928c2ecf20Sopenharmony_cierr_unmap_nandirq:
2938c2ecf20Sopenharmony_ci	irq_dispose_mapping(fsl_ifc_ctrl_dev->nand_irq);
2948c2ecf20Sopenharmony_ci	irq_dispose_mapping(fsl_ifc_ctrl_dev->irq);
2958c2ecf20Sopenharmony_cierr:
2968c2ecf20Sopenharmony_ci	iounmap(fsl_ifc_ctrl_dev->gregs);
2978c2ecf20Sopenharmony_ci	return ret;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic const struct of_device_id fsl_ifc_match[] = {
3018c2ecf20Sopenharmony_ci	{
3028c2ecf20Sopenharmony_ci		.compatible = "fsl,ifc",
3038c2ecf20Sopenharmony_ci	},
3048c2ecf20Sopenharmony_ci	{},
3058c2ecf20Sopenharmony_ci};
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic struct platform_driver fsl_ifc_ctrl_driver = {
3088c2ecf20Sopenharmony_ci	.driver = {
3098c2ecf20Sopenharmony_ci		.name	= "fsl-ifc",
3108c2ecf20Sopenharmony_ci		.of_match_table = fsl_ifc_match,
3118c2ecf20Sopenharmony_ci	},
3128c2ecf20Sopenharmony_ci	.probe       = fsl_ifc_ctrl_probe,
3138c2ecf20Sopenharmony_ci	.remove      = fsl_ifc_ctrl_remove,
3148c2ecf20Sopenharmony_ci};
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_cistatic int __init fsl_ifc_init(void)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	return platform_driver_register(&fsl_ifc_ctrl_driver);
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_cisubsys_initcall(fsl_ifc_init);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
3238c2ecf20Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor");
3248c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Freescale Integrated Flash Controller driver");
325