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