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