162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Freescale LBC and UPM routines. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright © 2007-2008 MontaVista Software, Inc. 662306a36Sopenharmony_ci * Copyright © 2010 Freescale Semiconductor 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: Anton Vorontsov <avorontsov@ru.mvista.com> 962306a36Sopenharmony_ci * Author: Jack Lan <Jack.Lan@freescale.com> 1062306a36Sopenharmony_ci * Author: Roy Zang <tie-fei.zang@freescale.com> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/export.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/compiler.h> 1762306a36Sopenharmony_ci#include <linux/spinlock.h> 1862306a36Sopenharmony_ci#include <linux/types.h> 1962306a36Sopenharmony_ci#include <linux/io.h> 2062306a36Sopenharmony_ci#include <linux/of.h> 2162306a36Sopenharmony_ci#include <linux/of_address.h> 2262306a36Sopenharmony_ci#include <linux/of_irq.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <linux/sched.h> 2562306a36Sopenharmony_ci#include <linux/platform_device.h> 2662306a36Sopenharmony_ci#include <linux/interrupt.h> 2762306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 2862306a36Sopenharmony_ci#include <linux/syscore_ops.h> 2962306a36Sopenharmony_ci#include <asm/fsl_lbc.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic DEFINE_SPINLOCK(fsl_lbc_lock); 3262306a36Sopenharmony_cistruct fsl_lbc_ctrl *fsl_lbc_ctrl_dev; 3362306a36Sopenharmony_ciEXPORT_SYMBOL(fsl_lbc_ctrl_dev); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/** 3662306a36Sopenharmony_ci * fsl_lbc_addr - convert the base address 3762306a36Sopenharmony_ci * @addr_base: base address of the memory bank 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * This function converts a base address of lbc into the right format for the 4062306a36Sopenharmony_ci * BR register. If the SOC has eLBC then it returns 32bit physical address 4162306a36Sopenharmony_ci * else it converts a 34bit local bus physical address to correct format of 4262306a36Sopenharmony_ci * 32bit address for BR register (Example: MPC8641). 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ciu32 fsl_lbc_addr(phys_addr_t addr_base) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct device_node *np = fsl_lbc_ctrl_dev->dev->of_node; 4762306a36Sopenharmony_ci u32 addr = addr_base & 0xffff8000; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (of_device_is_compatible(np, "fsl,elbc")) 5062306a36Sopenharmony_ci return addr; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci return addr | ((addr_base & 0x300000000ull) >> 19); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ciEXPORT_SYMBOL(fsl_lbc_addr); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/** 5762306a36Sopenharmony_ci * fsl_lbc_find - find Localbus bank 5862306a36Sopenharmony_ci * @addr_base: base address of the memory bank 5962306a36Sopenharmony_ci * 6062306a36Sopenharmony_ci * This function walks LBC banks comparing "Base address" field of the BR 6162306a36Sopenharmony_ci * registers with the supplied addr_base argument. When bases match this 6262306a36Sopenharmony_ci * function returns bank number (starting with 0), otherwise it returns 6362306a36Sopenharmony_ci * appropriate errno value. 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_ciint fsl_lbc_find(phys_addr_t addr_base) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci int i; 6862306a36Sopenharmony_ci struct fsl_lbc_regs __iomem *lbc; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) 7162306a36Sopenharmony_ci return -ENODEV; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci lbc = fsl_lbc_ctrl_dev->regs; 7462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(lbc->bank); i++) { 7562306a36Sopenharmony_ci u32 br = in_be32(&lbc->bank[i].br); 7662306a36Sopenharmony_ci u32 or = in_be32(&lbc->bank[i].or); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (br & BR_V && (br & or & BR_BA) == fsl_lbc_addr(addr_base)) 7962306a36Sopenharmony_ci return i; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return -ENOENT; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ciEXPORT_SYMBOL(fsl_lbc_find); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/** 8762306a36Sopenharmony_ci * fsl_upm_find - find pre-programmed UPM via base address 8862306a36Sopenharmony_ci * @addr_base: base address of the memory bank controlled by the UPM 8962306a36Sopenharmony_ci * @upm: pointer to the allocated fsl_upm structure 9062306a36Sopenharmony_ci * 9162306a36Sopenharmony_ci * This function fills fsl_upm structure so you can use it with the rest of 9262306a36Sopenharmony_ci * UPM API. On success this function returns 0, otherwise it returns 9362306a36Sopenharmony_ci * appropriate errno value. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ciint fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci int bank; 9862306a36Sopenharmony_ci u32 br; 9962306a36Sopenharmony_ci struct fsl_lbc_regs __iomem *lbc; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci bank = fsl_lbc_find(addr_base); 10262306a36Sopenharmony_ci if (bank < 0) 10362306a36Sopenharmony_ci return bank; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) 10662306a36Sopenharmony_ci return -ENODEV; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci lbc = fsl_lbc_ctrl_dev->regs; 10962306a36Sopenharmony_ci br = in_be32(&lbc->bank[bank].br); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci switch (br & BR_MSEL) { 11262306a36Sopenharmony_ci case BR_MS_UPMA: 11362306a36Sopenharmony_ci upm->mxmr = &lbc->mamr; 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci case BR_MS_UPMB: 11662306a36Sopenharmony_ci upm->mxmr = &lbc->mbmr; 11762306a36Sopenharmony_ci break; 11862306a36Sopenharmony_ci case BR_MS_UPMC: 11962306a36Sopenharmony_ci upm->mxmr = &lbc->mcmr; 12062306a36Sopenharmony_ci break; 12162306a36Sopenharmony_ci default: 12262306a36Sopenharmony_ci return -EINVAL; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci switch (br & BR_PS) { 12662306a36Sopenharmony_ci case BR_PS_8: 12762306a36Sopenharmony_ci upm->width = 8; 12862306a36Sopenharmony_ci break; 12962306a36Sopenharmony_ci case BR_PS_16: 13062306a36Sopenharmony_ci upm->width = 16; 13162306a36Sopenharmony_ci break; 13262306a36Sopenharmony_ci case BR_PS_32: 13362306a36Sopenharmony_ci upm->width = 32; 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci default: 13662306a36Sopenharmony_ci return -EINVAL; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ciEXPORT_SYMBOL(fsl_upm_find); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/** 14462306a36Sopenharmony_ci * fsl_upm_run_pattern - actually run an UPM pattern 14562306a36Sopenharmony_ci * @upm: pointer to the fsl_upm structure obtained via fsl_upm_find 14662306a36Sopenharmony_ci * @io_base: remapped pointer to where memory access should happen 14762306a36Sopenharmony_ci * @mar: MAR register content during pattern execution 14862306a36Sopenharmony_ci * 14962306a36Sopenharmony_ci * This function triggers dummy write to the memory specified by the io_base, 15062306a36Sopenharmony_ci * thus UPM pattern actually executed. Note that mar usage depends on the 15162306a36Sopenharmony_ci * pre-programmed AMX bits in the UPM RAM. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_ciint fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci int ret = 0; 15662306a36Sopenharmony_ci unsigned long flags; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) 15962306a36Sopenharmony_ci return -ENODEV; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci spin_lock_irqsave(&fsl_lbc_lock, flags); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci out_be32(&fsl_lbc_ctrl_dev->regs->mar, mar); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci switch (upm->width) { 16662306a36Sopenharmony_ci case 8: 16762306a36Sopenharmony_ci out_8(io_base, 0x0); 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci case 16: 17062306a36Sopenharmony_ci out_be16(io_base, 0x0); 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci case 32: 17362306a36Sopenharmony_ci out_be32(io_base, 0x0); 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci default: 17662306a36Sopenharmony_ci ret = -EINVAL; 17762306a36Sopenharmony_ci break; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci spin_unlock_irqrestore(&fsl_lbc_lock, flags); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return ret; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ciEXPORT_SYMBOL(fsl_upm_run_pattern); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic int fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl, 18762306a36Sopenharmony_ci struct device_node *node) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci struct fsl_lbc_regs __iomem *lbc = ctrl->regs; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* clear event registers */ 19262306a36Sopenharmony_ci setbits32(&lbc->ltesr, LTESR_CLEAR); 19362306a36Sopenharmony_ci out_be32(&lbc->lteatr, 0); 19462306a36Sopenharmony_ci out_be32(&lbc->ltear, 0); 19562306a36Sopenharmony_ci out_be32(&lbc->lteccr, LTECCR_CLEAR); 19662306a36Sopenharmony_ci out_be32(&lbc->ltedr, LTEDR_ENABLE); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* Set the monitor timeout value to the maximum for erratum A001 */ 19962306a36Sopenharmony_ci if (of_device_is_compatible(node, "fsl,elbc")) 20062306a36Sopenharmony_ci clrsetbits_be32(&lbc->lbcr, LBCR_BMT, LBCR_BMTPS); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return 0; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci/* 20662306a36Sopenharmony_ci * NOTE: This interrupt is used to report localbus events of various kinds, 20762306a36Sopenharmony_ci * such as transaction errors on the chipselects. 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic irqreturn_t fsl_lbc_ctrl_irq(int irqno, void *data) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct fsl_lbc_ctrl *ctrl = data; 21362306a36Sopenharmony_ci struct fsl_lbc_regs __iomem *lbc = ctrl->regs; 21462306a36Sopenharmony_ci u32 status; 21562306a36Sopenharmony_ci unsigned long flags; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci spin_lock_irqsave(&fsl_lbc_lock, flags); 21862306a36Sopenharmony_ci status = in_be32(&lbc->ltesr); 21962306a36Sopenharmony_ci if (!status) { 22062306a36Sopenharmony_ci spin_unlock_irqrestore(&fsl_lbc_lock, flags); 22162306a36Sopenharmony_ci return IRQ_NONE; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci out_be32(&lbc->ltesr, LTESR_CLEAR); 22562306a36Sopenharmony_ci out_be32(&lbc->lteatr, 0); 22662306a36Sopenharmony_ci out_be32(&lbc->ltear, 0); 22762306a36Sopenharmony_ci ctrl->irq_status = status; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (status & LTESR_BM) 23062306a36Sopenharmony_ci dev_err(ctrl->dev, "Local bus monitor time-out: " 23162306a36Sopenharmony_ci "LTESR 0x%08X\n", status); 23262306a36Sopenharmony_ci if (status & LTESR_WP) 23362306a36Sopenharmony_ci dev_err(ctrl->dev, "Write protect error: " 23462306a36Sopenharmony_ci "LTESR 0x%08X\n", status); 23562306a36Sopenharmony_ci if (status & LTESR_ATMW) 23662306a36Sopenharmony_ci dev_err(ctrl->dev, "Atomic write error: " 23762306a36Sopenharmony_ci "LTESR 0x%08X\n", status); 23862306a36Sopenharmony_ci if (status & LTESR_ATMR) 23962306a36Sopenharmony_ci dev_err(ctrl->dev, "Atomic read error: " 24062306a36Sopenharmony_ci "LTESR 0x%08X\n", status); 24162306a36Sopenharmony_ci if (status & LTESR_CS) 24262306a36Sopenharmony_ci dev_err(ctrl->dev, "Chip select error: " 24362306a36Sopenharmony_ci "LTESR 0x%08X\n", status); 24462306a36Sopenharmony_ci if (status & LTESR_FCT) { 24562306a36Sopenharmony_ci dev_err(ctrl->dev, "FCM command time-out: " 24662306a36Sopenharmony_ci "LTESR 0x%08X\n", status); 24762306a36Sopenharmony_ci smp_wmb(); 24862306a36Sopenharmony_ci wake_up(&ctrl->irq_wait); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci if (status & LTESR_PAR) { 25162306a36Sopenharmony_ci dev_err(ctrl->dev, "Parity or Uncorrectable ECC error: " 25262306a36Sopenharmony_ci "LTESR 0x%08X\n", status); 25362306a36Sopenharmony_ci smp_wmb(); 25462306a36Sopenharmony_ci wake_up(&ctrl->irq_wait); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci if (status & LTESR_CC) { 25762306a36Sopenharmony_ci smp_wmb(); 25862306a36Sopenharmony_ci wake_up(&ctrl->irq_wait); 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci if (status & ~LTESR_MASK) 26162306a36Sopenharmony_ci dev_err(ctrl->dev, "Unknown error: " 26262306a36Sopenharmony_ci "LTESR 0x%08X\n", status); 26362306a36Sopenharmony_ci spin_unlock_irqrestore(&fsl_lbc_lock, flags); 26462306a36Sopenharmony_ci return IRQ_HANDLED; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/* 26862306a36Sopenharmony_ci * fsl_lbc_ctrl_probe 26962306a36Sopenharmony_ci * 27062306a36Sopenharmony_ci * called by device layer when it finds a device matching 27162306a36Sopenharmony_ci * one our driver can handled. This code allocates all of 27262306a36Sopenharmony_ci * the resources needed for the controller only. The 27362306a36Sopenharmony_ci * resources for the NAND banks themselves are allocated 27462306a36Sopenharmony_ci * in the chip probe function. 27562306a36Sopenharmony_ci*/ 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic int fsl_lbc_ctrl_probe(struct platform_device *dev) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci int ret; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (!dev->dev.of_node) { 28262306a36Sopenharmony_ci dev_err(&dev->dev, "Device OF-Node is NULL"); 28362306a36Sopenharmony_ci return -EFAULT; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci fsl_lbc_ctrl_dev = kzalloc(sizeof(*fsl_lbc_ctrl_dev), GFP_KERNEL); 28762306a36Sopenharmony_ci if (!fsl_lbc_ctrl_dev) 28862306a36Sopenharmony_ci return -ENOMEM; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci dev_set_drvdata(&dev->dev, fsl_lbc_ctrl_dev); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci spin_lock_init(&fsl_lbc_ctrl_dev->lock); 29362306a36Sopenharmony_ci init_waitqueue_head(&fsl_lbc_ctrl_dev->irq_wait); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci fsl_lbc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0); 29662306a36Sopenharmony_ci if (!fsl_lbc_ctrl_dev->regs) { 29762306a36Sopenharmony_ci dev_err(&dev->dev, "failed to get memory region\n"); 29862306a36Sopenharmony_ci ret = -ENODEV; 29962306a36Sopenharmony_ci goto err; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci fsl_lbc_ctrl_dev->irq[0] = irq_of_parse_and_map(dev->dev.of_node, 0); 30362306a36Sopenharmony_ci if (!fsl_lbc_ctrl_dev->irq[0]) { 30462306a36Sopenharmony_ci dev_err(&dev->dev, "failed to get irq resource\n"); 30562306a36Sopenharmony_ci ret = -ENODEV; 30662306a36Sopenharmony_ci goto err; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci fsl_lbc_ctrl_dev->dev = &dev->dev; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci ret = fsl_lbc_ctrl_init(fsl_lbc_ctrl_dev, dev->dev.of_node); 31262306a36Sopenharmony_ci if (ret < 0) 31362306a36Sopenharmony_ci goto err; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci ret = request_irq(fsl_lbc_ctrl_dev->irq[0], fsl_lbc_ctrl_irq, 0, 31662306a36Sopenharmony_ci "fsl-lbc", fsl_lbc_ctrl_dev); 31762306a36Sopenharmony_ci if (ret != 0) { 31862306a36Sopenharmony_ci dev_err(&dev->dev, "failed to install irq (%d)\n", 31962306a36Sopenharmony_ci fsl_lbc_ctrl_dev->irq[0]); 32062306a36Sopenharmony_ci ret = fsl_lbc_ctrl_dev->irq[0]; 32162306a36Sopenharmony_ci goto err; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci fsl_lbc_ctrl_dev->irq[1] = irq_of_parse_and_map(dev->dev.of_node, 1); 32562306a36Sopenharmony_ci if (fsl_lbc_ctrl_dev->irq[1]) { 32662306a36Sopenharmony_ci ret = request_irq(fsl_lbc_ctrl_dev->irq[1], fsl_lbc_ctrl_irq, 32762306a36Sopenharmony_ci IRQF_SHARED, "fsl-lbc-err", fsl_lbc_ctrl_dev); 32862306a36Sopenharmony_ci if (ret) { 32962306a36Sopenharmony_ci dev_err(&dev->dev, "failed to install irq (%d)\n", 33062306a36Sopenharmony_ci fsl_lbc_ctrl_dev->irq[1]); 33162306a36Sopenharmony_ci ret = fsl_lbc_ctrl_dev->irq[1]; 33262306a36Sopenharmony_ci goto err1; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* Enable interrupts for any detected events */ 33762306a36Sopenharmony_ci out_be32(&fsl_lbc_ctrl_dev->regs->lteir, LTEIR_ENABLE); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci return 0; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cierr1: 34262306a36Sopenharmony_ci free_irq(fsl_lbc_ctrl_dev->irq[0], fsl_lbc_ctrl_dev); 34362306a36Sopenharmony_cierr: 34462306a36Sopenharmony_ci iounmap(fsl_lbc_ctrl_dev->regs); 34562306a36Sopenharmony_ci kfree(fsl_lbc_ctrl_dev); 34662306a36Sopenharmony_ci fsl_lbc_ctrl_dev = NULL; 34762306a36Sopenharmony_ci return ret; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci#ifdef CONFIG_SUSPEND 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci/* save lbc registers */ 35362306a36Sopenharmony_cistatic int fsl_lbc_syscore_suspend(void) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct fsl_lbc_ctrl *ctrl; 35662306a36Sopenharmony_ci struct fsl_lbc_regs __iomem *lbc; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci ctrl = fsl_lbc_ctrl_dev; 35962306a36Sopenharmony_ci if (!ctrl) 36062306a36Sopenharmony_ci goto out; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci lbc = ctrl->regs; 36362306a36Sopenharmony_ci if (!lbc) 36462306a36Sopenharmony_ci goto out; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci ctrl->saved_regs = kmalloc(sizeof(struct fsl_lbc_regs), GFP_KERNEL); 36762306a36Sopenharmony_ci if (!ctrl->saved_regs) 36862306a36Sopenharmony_ci return -ENOMEM; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci _memcpy_fromio(ctrl->saved_regs, lbc, sizeof(struct fsl_lbc_regs)); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ciout: 37362306a36Sopenharmony_ci return 0; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci/* restore lbc registers */ 37762306a36Sopenharmony_cistatic void fsl_lbc_syscore_resume(void) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct fsl_lbc_ctrl *ctrl; 38062306a36Sopenharmony_ci struct fsl_lbc_regs __iomem *lbc; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci ctrl = fsl_lbc_ctrl_dev; 38362306a36Sopenharmony_ci if (!ctrl) 38462306a36Sopenharmony_ci goto out; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci lbc = ctrl->regs; 38762306a36Sopenharmony_ci if (!lbc) 38862306a36Sopenharmony_ci goto out; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (ctrl->saved_regs) { 39162306a36Sopenharmony_ci _memcpy_toio(lbc, ctrl->saved_regs, 39262306a36Sopenharmony_ci sizeof(struct fsl_lbc_regs)); 39362306a36Sopenharmony_ci kfree(ctrl->saved_regs); 39462306a36Sopenharmony_ci ctrl->saved_regs = NULL; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ciout: 39862306a36Sopenharmony_ci return; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci#endif /* CONFIG_SUSPEND */ 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic const struct of_device_id fsl_lbc_match[] = { 40362306a36Sopenharmony_ci { .compatible = "fsl,elbc", }, 40462306a36Sopenharmony_ci { .compatible = "fsl,pq3-localbus", }, 40562306a36Sopenharmony_ci { .compatible = "fsl,pq2-localbus", }, 40662306a36Sopenharmony_ci { .compatible = "fsl,pq2pro-localbus", }, 40762306a36Sopenharmony_ci {}, 40862306a36Sopenharmony_ci}; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci#ifdef CONFIG_SUSPEND 41162306a36Sopenharmony_cistatic struct syscore_ops lbc_syscore_pm_ops = { 41262306a36Sopenharmony_ci .suspend = fsl_lbc_syscore_suspend, 41362306a36Sopenharmony_ci .resume = fsl_lbc_syscore_resume, 41462306a36Sopenharmony_ci}; 41562306a36Sopenharmony_ci#endif 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic struct platform_driver fsl_lbc_ctrl_driver = { 41862306a36Sopenharmony_ci .driver = { 41962306a36Sopenharmony_ci .name = "fsl-lbc", 42062306a36Sopenharmony_ci .of_match_table = fsl_lbc_match, 42162306a36Sopenharmony_ci }, 42262306a36Sopenharmony_ci .probe = fsl_lbc_ctrl_probe, 42362306a36Sopenharmony_ci}; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic int __init fsl_lbc_init(void) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci#ifdef CONFIG_SUSPEND 42862306a36Sopenharmony_ci register_syscore_ops(&lbc_syscore_pm_ops); 42962306a36Sopenharmony_ci#endif 43062306a36Sopenharmony_ci return platform_driver_register(&fsl_lbc_ctrl_driver); 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_cisubsys_initcall(fsl_lbc_init); 433