18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Freescale LBC and UPM routines.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright © 2007-2008  MontaVista Software, Inc.
68c2ecf20Sopenharmony_ci * Copyright © 2010 Freescale Semiconductor
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
98c2ecf20Sopenharmony_ci * Author: Jack Lan <Jack.Lan@freescale.com>
108c2ecf20Sopenharmony_ci * Author: Roy Zang <tie-fei.zang@freescale.com>
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/export.h>
158c2ecf20Sopenharmony_ci#include <linux/kernel.h>
168c2ecf20Sopenharmony_ci#include <linux/compiler.h>
178c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
188c2ecf20Sopenharmony_ci#include <linux/types.h>
198c2ecf20Sopenharmony_ci#include <linux/io.h>
208c2ecf20Sopenharmony_ci#include <linux/of.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci#include <linux/sched.h>
238c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
248c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
258c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h>
268c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h>
278c2ecf20Sopenharmony_ci#include <asm/prom.h>
288c2ecf20Sopenharmony_ci#include <asm/fsl_lbc.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(fsl_lbc_lock);
318c2ecf20Sopenharmony_cistruct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fsl_lbc_ctrl_dev);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/**
358c2ecf20Sopenharmony_ci * fsl_lbc_addr - convert the base address
368c2ecf20Sopenharmony_ci * @addr_base:	base address of the memory bank
378c2ecf20Sopenharmony_ci *
388c2ecf20Sopenharmony_ci * This function converts a base address of lbc into the right format for the
398c2ecf20Sopenharmony_ci * BR register. If the SOC has eLBC then it returns 32bit physical address
408c2ecf20Sopenharmony_ci * else it convers a 34bit local bus physical address to correct format of
418c2ecf20Sopenharmony_ci * 32bit address for BR register (Example: MPC8641).
428c2ecf20Sopenharmony_ci */
438c2ecf20Sopenharmony_ciu32 fsl_lbc_addr(phys_addr_t addr_base)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct device_node *np = fsl_lbc_ctrl_dev->dev->of_node;
468c2ecf20Sopenharmony_ci	u32 addr = addr_base & 0xffff8000;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	if (of_device_is_compatible(np, "fsl,elbc"))
498c2ecf20Sopenharmony_ci		return addr;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	return addr | ((addr_base & 0x300000000ull) >> 19);
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fsl_lbc_addr);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/**
568c2ecf20Sopenharmony_ci * fsl_lbc_find - find Localbus bank
578c2ecf20Sopenharmony_ci * @addr_base:	base address of the memory bank
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci * This function walks LBC banks comparing "Base address" field of the BR
608c2ecf20Sopenharmony_ci * registers with the supplied addr_base argument. When bases match this
618c2ecf20Sopenharmony_ci * function returns bank number (starting with 0), otherwise it returns
628c2ecf20Sopenharmony_ci * appropriate errno value.
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_ciint fsl_lbc_find(phys_addr_t addr_base)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	int i;
678c2ecf20Sopenharmony_ci	struct fsl_lbc_regs __iomem *lbc;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
708c2ecf20Sopenharmony_ci		return -ENODEV;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	lbc = fsl_lbc_ctrl_dev->regs;
738c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(lbc->bank); i++) {
748c2ecf20Sopenharmony_ci		u32 br = in_be32(&lbc->bank[i].br);
758c2ecf20Sopenharmony_ci		u32 or = in_be32(&lbc->bank[i].or);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci		if (br & BR_V && (br & or & BR_BA) == fsl_lbc_addr(addr_base))
788c2ecf20Sopenharmony_ci			return i;
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	return -ENOENT;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fsl_lbc_find);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/**
868c2ecf20Sopenharmony_ci * fsl_upm_find - find pre-programmed UPM via base address
878c2ecf20Sopenharmony_ci * @addr_base:	base address of the memory bank controlled by the UPM
888c2ecf20Sopenharmony_ci * @upm:	pointer to the allocated fsl_upm structure
898c2ecf20Sopenharmony_ci *
908c2ecf20Sopenharmony_ci * This function fills fsl_upm structure so you can use it with the rest of
918c2ecf20Sopenharmony_ci * UPM API. On success this function returns 0, otherwise it returns
928c2ecf20Sopenharmony_ci * appropriate errno value.
938c2ecf20Sopenharmony_ci */
948c2ecf20Sopenharmony_ciint fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	int bank;
978c2ecf20Sopenharmony_ci	u32 br;
988c2ecf20Sopenharmony_ci	struct fsl_lbc_regs __iomem *lbc;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	bank = fsl_lbc_find(addr_base);
1018c2ecf20Sopenharmony_ci	if (bank < 0)
1028c2ecf20Sopenharmony_ci		return bank;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
1058c2ecf20Sopenharmony_ci		return -ENODEV;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	lbc = fsl_lbc_ctrl_dev->regs;
1088c2ecf20Sopenharmony_ci	br = in_be32(&lbc->bank[bank].br);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	switch (br & BR_MSEL) {
1118c2ecf20Sopenharmony_ci	case BR_MS_UPMA:
1128c2ecf20Sopenharmony_ci		upm->mxmr = &lbc->mamr;
1138c2ecf20Sopenharmony_ci		break;
1148c2ecf20Sopenharmony_ci	case BR_MS_UPMB:
1158c2ecf20Sopenharmony_ci		upm->mxmr = &lbc->mbmr;
1168c2ecf20Sopenharmony_ci		break;
1178c2ecf20Sopenharmony_ci	case BR_MS_UPMC:
1188c2ecf20Sopenharmony_ci		upm->mxmr = &lbc->mcmr;
1198c2ecf20Sopenharmony_ci		break;
1208c2ecf20Sopenharmony_ci	default:
1218c2ecf20Sopenharmony_ci		return -EINVAL;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	switch (br & BR_PS) {
1258c2ecf20Sopenharmony_ci	case BR_PS_8:
1268c2ecf20Sopenharmony_ci		upm->width = 8;
1278c2ecf20Sopenharmony_ci		break;
1288c2ecf20Sopenharmony_ci	case BR_PS_16:
1298c2ecf20Sopenharmony_ci		upm->width = 16;
1308c2ecf20Sopenharmony_ci		break;
1318c2ecf20Sopenharmony_ci	case BR_PS_32:
1328c2ecf20Sopenharmony_ci		upm->width = 32;
1338c2ecf20Sopenharmony_ci		break;
1348c2ecf20Sopenharmony_ci	default:
1358c2ecf20Sopenharmony_ci		return -EINVAL;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	return 0;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fsl_upm_find);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci/**
1438c2ecf20Sopenharmony_ci * fsl_upm_run_pattern - actually run an UPM pattern
1448c2ecf20Sopenharmony_ci * @upm:	pointer to the fsl_upm structure obtained via fsl_upm_find
1458c2ecf20Sopenharmony_ci * @io_base:	remapped pointer to where memory access should happen
1468c2ecf20Sopenharmony_ci * @mar:	MAR register content during pattern execution
1478c2ecf20Sopenharmony_ci *
1488c2ecf20Sopenharmony_ci * This function triggers dummy write to the memory specified by the io_base,
1498c2ecf20Sopenharmony_ci * thus UPM pattern actually executed. Note that mar usage depends on the
1508c2ecf20Sopenharmony_ci * pre-programmed AMX bits in the UPM RAM.
1518c2ecf20Sopenharmony_ci */
1528c2ecf20Sopenharmony_ciint fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	int ret = 0;
1558c2ecf20Sopenharmony_ci	unsigned long flags;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
1588c2ecf20Sopenharmony_ci		return -ENODEV;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fsl_lbc_lock, flags);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	out_be32(&fsl_lbc_ctrl_dev->regs->mar, mar);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	switch (upm->width) {
1658c2ecf20Sopenharmony_ci	case 8:
1668c2ecf20Sopenharmony_ci		out_8(io_base, 0x0);
1678c2ecf20Sopenharmony_ci		break;
1688c2ecf20Sopenharmony_ci	case 16:
1698c2ecf20Sopenharmony_ci		out_be16(io_base, 0x0);
1708c2ecf20Sopenharmony_ci		break;
1718c2ecf20Sopenharmony_ci	case 32:
1728c2ecf20Sopenharmony_ci		out_be32(io_base, 0x0);
1738c2ecf20Sopenharmony_ci		break;
1748c2ecf20Sopenharmony_ci	default:
1758c2ecf20Sopenharmony_ci		ret = -EINVAL;
1768c2ecf20Sopenharmony_ci		break;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fsl_lbc_lock, flags);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	return ret;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fsl_upm_run_pattern);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl,
1868c2ecf20Sopenharmony_ci			     struct device_node *node)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	/* clear event registers */
1918c2ecf20Sopenharmony_ci	setbits32(&lbc->ltesr, LTESR_CLEAR);
1928c2ecf20Sopenharmony_ci	out_be32(&lbc->lteatr, 0);
1938c2ecf20Sopenharmony_ci	out_be32(&lbc->ltear, 0);
1948c2ecf20Sopenharmony_ci	out_be32(&lbc->lteccr, LTECCR_CLEAR);
1958c2ecf20Sopenharmony_ci	out_be32(&lbc->ltedr, LTEDR_ENABLE);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	/* Set the monitor timeout value to the maximum for erratum A001 */
1988c2ecf20Sopenharmony_ci	if (of_device_is_compatible(node, "fsl,elbc"))
1998c2ecf20Sopenharmony_ci		clrsetbits_be32(&lbc->lbcr, LBCR_BMT, LBCR_BMTPS);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	return 0;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci/*
2058c2ecf20Sopenharmony_ci * NOTE: This interrupt is used to report localbus events of various kinds,
2068c2ecf20Sopenharmony_ci * such as transaction errors on the chipselects.
2078c2ecf20Sopenharmony_ci */
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic irqreturn_t fsl_lbc_ctrl_irq(int irqno, void *data)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct fsl_lbc_ctrl *ctrl = data;
2128c2ecf20Sopenharmony_ci	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
2138c2ecf20Sopenharmony_ci	u32 status;
2148c2ecf20Sopenharmony_ci	unsigned long flags;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	spin_lock_irqsave(&fsl_lbc_lock, flags);
2178c2ecf20Sopenharmony_ci	status = in_be32(&lbc->ltesr);
2188c2ecf20Sopenharmony_ci	if (!status) {
2198c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fsl_lbc_lock, flags);
2208c2ecf20Sopenharmony_ci		return IRQ_NONE;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	out_be32(&lbc->ltesr, LTESR_CLEAR);
2248c2ecf20Sopenharmony_ci	out_be32(&lbc->lteatr, 0);
2258c2ecf20Sopenharmony_ci	out_be32(&lbc->ltear, 0);
2268c2ecf20Sopenharmony_ci	ctrl->irq_status = status;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (status & LTESR_BM)
2298c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "Local bus monitor time-out: "
2308c2ecf20Sopenharmony_ci			"LTESR 0x%08X\n", status);
2318c2ecf20Sopenharmony_ci	if (status & LTESR_WP)
2328c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "Write protect error: "
2338c2ecf20Sopenharmony_ci			"LTESR 0x%08X\n", status);
2348c2ecf20Sopenharmony_ci	if (status & LTESR_ATMW)
2358c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "Atomic write error: "
2368c2ecf20Sopenharmony_ci			"LTESR 0x%08X\n", status);
2378c2ecf20Sopenharmony_ci	if (status & LTESR_ATMR)
2388c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "Atomic read error: "
2398c2ecf20Sopenharmony_ci			"LTESR 0x%08X\n", status);
2408c2ecf20Sopenharmony_ci	if (status & LTESR_CS)
2418c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "Chip select error: "
2428c2ecf20Sopenharmony_ci			"LTESR 0x%08X\n", status);
2438c2ecf20Sopenharmony_ci	if (status & LTESR_FCT) {
2448c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "FCM command time-out: "
2458c2ecf20Sopenharmony_ci			"LTESR 0x%08X\n", status);
2468c2ecf20Sopenharmony_ci		smp_wmb();
2478c2ecf20Sopenharmony_ci		wake_up(&ctrl->irq_wait);
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci	if (status & LTESR_PAR) {
2508c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "Parity or Uncorrectable ECC error: "
2518c2ecf20Sopenharmony_ci			"LTESR 0x%08X\n", status);
2528c2ecf20Sopenharmony_ci		smp_wmb();
2538c2ecf20Sopenharmony_ci		wake_up(&ctrl->irq_wait);
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci	if (status & LTESR_CC) {
2568c2ecf20Sopenharmony_ci		smp_wmb();
2578c2ecf20Sopenharmony_ci		wake_up(&ctrl->irq_wait);
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci	if (status & ~LTESR_MASK)
2608c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "Unknown error: "
2618c2ecf20Sopenharmony_ci			"LTESR 0x%08X\n", status);
2628c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&fsl_lbc_lock, flags);
2638c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci/*
2678c2ecf20Sopenharmony_ci * fsl_lbc_ctrl_probe
2688c2ecf20Sopenharmony_ci *
2698c2ecf20Sopenharmony_ci * called by device layer when it finds a device matching
2708c2ecf20Sopenharmony_ci * one our driver can handled. This code allocates all of
2718c2ecf20Sopenharmony_ci * the resources needed for the controller only.  The
2728c2ecf20Sopenharmony_ci * resources for the NAND banks themselves are allocated
2738c2ecf20Sopenharmony_ci * in the chip probe function.
2748c2ecf20Sopenharmony_ci*/
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic int fsl_lbc_ctrl_probe(struct platform_device *dev)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	int ret;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (!dev->dev.of_node) {
2818c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "Device OF-Node is NULL");
2828c2ecf20Sopenharmony_ci		return -EFAULT;
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	fsl_lbc_ctrl_dev = kzalloc(sizeof(*fsl_lbc_ctrl_dev), GFP_KERNEL);
2868c2ecf20Sopenharmony_ci	if (!fsl_lbc_ctrl_dev)
2878c2ecf20Sopenharmony_ci		return -ENOMEM;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	dev_set_drvdata(&dev->dev, fsl_lbc_ctrl_dev);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	spin_lock_init(&fsl_lbc_ctrl_dev->lock);
2928c2ecf20Sopenharmony_ci	init_waitqueue_head(&fsl_lbc_ctrl_dev->irq_wait);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	fsl_lbc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0);
2958c2ecf20Sopenharmony_ci	if (!fsl_lbc_ctrl_dev->regs) {
2968c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "failed to get memory region\n");
2978c2ecf20Sopenharmony_ci		ret = -ENODEV;
2988c2ecf20Sopenharmony_ci		goto err;
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	fsl_lbc_ctrl_dev->irq[0] = irq_of_parse_and_map(dev->dev.of_node, 0);
3028c2ecf20Sopenharmony_ci	if (!fsl_lbc_ctrl_dev->irq[0]) {
3038c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "failed to get irq resource\n");
3048c2ecf20Sopenharmony_ci		ret = -ENODEV;
3058c2ecf20Sopenharmony_ci		goto err;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	fsl_lbc_ctrl_dev->dev = &dev->dev;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	ret = fsl_lbc_ctrl_init(fsl_lbc_ctrl_dev, dev->dev.of_node);
3118c2ecf20Sopenharmony_ci	if (ret < 0)
3128c2ecf20Sopenharmony_ci		goto err;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	ret = request_irq(fsl_lbc_ctrl_dev->irq[0], fsl_lbc_ctrl_irq, 0,
3158c2ecf20Sopenharmony_ci				"fsl-lbc", fsl_lbc_ctrl_dev);
3168c2ecf20Sopenharmony_ci	if (ret != 0) {
3178c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "failed to install irq (%d)\n",
3188c2ecf20Sopenharmony_ci			fsl_lbc_ctrl_dev->irq[0]);
3198c2ecf20Sopenharmony_ci		ret = fsl_lbc_ctrl_dev->irq[0];
3208c2ecf20Sopenharmony_ci		goto err;
3218c2ecf20Sopenharmony_ci	}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	fsl_lbc_ctrl_dev->irq[1] = irq_of_parse_and_map(dev->dev.of_node, 1);
3248c2ecf20Sopenharmony_ci	if (fsl_lbc_ctrl_dev->irq[1]) {
3258c2ecf20Sopenharmony_ci		ret = request_irq(fsl_lbc_ctrl_dev->irq[1], fsl_lbc_ctrl_irq,
3268c2ecf20Sopenharmony_ci				IRQF_SHARED, "fsl-lbc-err", fsl_lbc_ctrl_dev);
3278c2ecf20Sopenharmony_ci		if (ret) {
3288c2ecf20Sopenharmony_ci			dev_err(&dev->dev, "failed to install irq (%d)\n",
3298c2ecf20Sopenharmony_ci					fsl_lbc_ctrl_dev->irq[1]);
3308c2ecf20Sopenharmony_ci			ret = fsl_lbc_ctrl_dev->irq[1];
3318c2ecf20Sopenharmony_ci			goto err1;
3328c2ecf20Sopenharmony_ci		}
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/* Enable interrupts for any detected events */
3368c2ecf20Sopenharmony_ci	out_be32(&fsl_lbc_ctrl_dev->regs->lteir, LTEIR_ENABLE);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	return 0;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cierr1:
3418c2ecf20Sopenharmony_ci	free_irq(fsl_lbc_ctrl_dev->irq[0], fsl_lbc_ctrl_dev);
3428c2ecf20Sopenharmony_cierr:
3438c2ecf20Sopenharmony_ci	iounmap(fsl_lbc_ctrl_dev->regs);
3448c2ecf20Sopenharmony_ci	kfree(fsl_lbc_ctrl_dev);
3458c2ecf20Sopenharmony_ci	fsl_lbc_ctrl_dev = NULL;
3468c2ecf20Sopenharmony_ci	return ret;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci#ifdef CONFIG_SUSPEND
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci/* save lbc registers */
3528c2ecf20Sopenharmony_cistatic int fsl_lbc_syscore_suspend(void)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	struct fsl_lbc_ctrl *ctrl;
3558c2ecf20Sopenharmony_ci	struct fsl_lbc_regs __iomem *lbc;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	ctrl = fsl_lbc_ctrl_dev;
3588c2ecf20Sopenharmony_ci	if (!ctrl)
3598c2ecf20Sopenharmony_ci		goto out;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	lbc = ctrl->regs;
3628c2ecf20Sopenharmony_ci	if (!lbc)
3638c2ecf20Sopenharmony_ci		goto out;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	ctrl->saved_regs = kmalloc(sizeof(struct fsl_lbc_regs), GFP_KERNEL);
3668c2ecf20Sopenharmony_ci	if (!ctrl->saved_regs)
3678c2ecf20Sopenharmony_ci		return -ENOMEM;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	_memcpy_fromio(ctrl->saved_regs, lbc, sizeof(struct fsl_lbc_regs));
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ciout:
3728c2ecf20Sopenharmony_ci	return 0;
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci/* restore lbc registers */
3768c2ecf20Sopenharmony_cistatic void fsl_lbc_syscore_resume(void)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	struct fsl_lbc_ctrl *ctrl;
3798c2ecf20Sopenharmony_ci	struct fsl_lbc_regs __iomem *lbc;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	ctrl = fsl_lbc_ctrl_dev;
3828c2ecf20Sopenharmony_ci	if (!ctrl)
3838c2ecf20Sopenharmony_ci		goto out;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	lbc = ctrl->regs;
3868c2ecf20Sopenharmony_ci	if (!lbc)
3878c2ecf20Sopenharmony_ci		goto out;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	if (ctrl->saved_regs) {
3908c2ecf20Sopenharmony_ci		_memcpy_toio(lbc, ctrl->saved_regs,
3918c2ecf20Sopenharmony_ci				sizeof(struct fsl_lbc_regs));
3928c2ecf20Sopenharmony_ci		kfree(ctrl->saved_regs);
3938c2ecf20Sopenharmony_ci		ctrl->saved_regs = NULL;
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ciout:
3978c2ecf20Sopenharmony_ci	return;
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci#endif /* CONFIG_SUSPEND */
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic const struct of_device_id fsl_lbc_match[] = {
4028c2ecf20Sopenharmony_ci	{ .compatible = "fsl,elbc", },
4038c2ecf20Sopenharmony_ci	{ .compatible = "fsl,pq3-localbus", },
4048c2ecf20Sopenharmony_ci	{ .compatible = "fsl,pq2-localbus", },
4058c2ecf20Sopenharmony_ci	{ .compatible = "fsl,pq2pro-localbus", },
4068c2ecf20Sopenharmony_ci	{},
4078c2ecf20Sopenharmony_ci};
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci#ifdef CONFIG_SUSPEND
4108c2ecf20Sopenharmony_cistatic struct syscore_ops lbc_syscore_pm_ops = {
4118c2ecf20Sopenharmony_ci	.suspend = fsl_lbc_syscore_suspend,
4128c2ecf20Sopenharmony_ci	.resume = fsl_lbc_syscore_resume,
4138c2ecf20Sopenharmony_ci};
4148c2ecf20Sopenharmony_ci#endif
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic struct platform_driver fsl_lbc_ctrl_driver = {
4178c2ecf20Sopenharmony_ci	.driver = {
4188c2ecf20Sopenharmony_ci		.name = "fsl-lbc",
4198c2ecf20Sopenharmony_ci		.of_match_table = fsl_lbc_match,
4208c2ecf20Sopenharmony_ci	},
4218c2ecf20Sopenharmony_ci	.probe = fsl_lbc_ctrl_probe,
4228c2ecf20Sopenharmony_ci};
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cistatic int __init fsl_lbc_init(void)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci#ifdef CONFIG_SUSPEND
4278c2ecf20Sopenharmony_ci	register_syscore_ops(&lbc_syscore_pm_ops);
4288c2ecf20Sopenharmony_ci#endif
4298c2ecf20Sopenharmony_ci	return platform_driver_register(&fsl_lbc_ctrl_driver);
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_cisubsys_initcall(fsl_lbc_init);
432