1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Freescale LBC and UPM routines.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright © 2007-2008 MontaVista Software, Inc.
6*4882a593Smuzhiyun * Copyright © 2010 Freescale Semiconductor
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
9*4882a593Smuzhiyun * Author: Jack Lan <Jack.Lan@freescale.com>
10*4882a593Smuzhiyun * Author: Roy Zang <tie-fei.zang@freescale.com>
11*4882a593Smuzhiyun */
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun #include <linux/init.h>
14*4882a593Smuzhiyun #include <linux/export.h>
15*4882a593Smuzhiyun #include <linux/kernel.h>
16*4882a593Smuzhiyun #include <linux/compiler.h>
17*4882a593Smuzhiyun #include <linux/spinlock.h>
18*4882a593Smuzhiyun #include <linux/types.h>
19*4882a593Smuzhiyun #include <linux/io.h>
20*4882a593Smuzhiyun #include <linux/of.h>
21*4882a593Smuzhiyun #include <linux/slab.h>
22*4882a593Smuzhiyun #include <linux/sched.h>
23*4882a593Smuzhiyun #include <linux/platform_device.h>
24*4882a593Smuzhiyun #include <linux/interrupt.h>
25*4882a593Smuzhiyun #include <linux/mod_devicetable.h>
26*4882a593Smuzhiyun #include <linux/syscore_ops.h>
27*4882a593Smuzhiyun #include <asm/prom.h>
28*4882a593Smuzhiyun #include <asm/fsl_lbc.h>
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun static DEFINE_SPINLOCK(fsl_lbc_lock);
31*4882a593Smuzhiyun struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
32*4882a593Smuzhiyun EXPORT_SYMBOL(fsl_lbc_ctrl_dev);
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun /**
35*4882a593Smuzhiyun * fsl_lbc_addr - convert the base address
36*4882a593Smuzhiyun * @addr_base: base address of the memory bank
37*4882a593Smuzhiyun *
38*4882a593Smuzhiyun * This function converts a base address of lbc into the right format for the
39*4882a593Smuzhiyun * BR register. If the SOC has eLBC then it returns 32bit physical address
40*4882a593Smuzhiyun * else it convers a 34bit local bus physical address to correct format of
41*4882a593Smuzhiyun * 32bit address for BR register (Example: MPC8641).
42*4882a593Smuzhiyun */
fsl_lbc_addr(phys_addr_t addr_base)43*4882a593Smuzhiyun u32 fsl_lbc_addr(phys_addr_t addr_base)
44*4882a593Smuzhiyun {
45*4882a593Smuzhiyun struct device_node *np = fsl_lbc_ctrl_dev->dev->of_node;
46*4882a593Smuzhiyun u32 addr = addr_base & 0xffff8000;
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun if (of_device_is_compatible(np, "fsl,elbc"))
49*4882a593Smuzhiyun return addr;
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun return addr | ((addr_base & 0x300000000ull) >> 19);
52*4882a593Smuzhiyun }
53*4882a593Smuzhiyun EXPORT_SYMBOL(fsl_lbc_addr);
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun /**
56*4882a593Smuzhiyun * fsl_lbc_find - find Localbus bank
57*4882a593Smuzhiyun * @addr_base: base address of the memory bank
58*4882a593Smuzhiyun *
59*4882a593Smuzhiyun * This function walks LBC banks comparing "Base address" field of the BR
60*4882a593Smuzhiyun * registers with the supplied addr_base argument. When bases match this
61*4882a593Smuzhiyun * function returns bank number (starting with 0), otherwise it returns
62*4882a593Smuzhiyun * appropriate errno value.
63*4882a593Smuzhiyun */
fsl_lbc_find(phys_addr_t addr_base)64*4882a593Smuzhiyun int fsl_lbc_find(phys_addr_t addr_base)
65*4882a593Smuzhiyun {
66*4882a593Smuzhiyun int i;
67*4882a593Smuzhiyun struct fsl_lbc_regs __iomem *lbc;
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
70*4882a593Smuzhiyun return -ENODEV;
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun lbc = fsl_lbc_ctrl_dev->regs;
73*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(lbc->bank); i++) {
74*4882a593Smuzhiyun u32 br = in_be32(&lbc->bank[i].br);
75*4882a593Smuzhiyun u32 or = in_be32(&lbc->bank[i].or);
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun if (br & BR_V && (br & or & BR_BA) == fsl_lbc_addr(addr_base))
78*4882a593Smuzhiyun return i;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun return -ENOENT;
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun EXPORT_SYMBOL(fsl_lbc_find);
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun /**
86*4882a593Smuzhiyun * fsl_upm_find - find pre-programmed UPM via base address
87*4882a593Smuzhiyun * @addr_base: base address of the memory bank controlled by the UPM
88*4882a593Smuzhiyun * @upm: pointer to the allocated fsl_upm structure
89*4882a593Smuzhiyun *
90*4882a593Smuzhiyun * This function fills fsl_upm structure so you can use it with the rest of
91*4882a593Smuzhiyun * UPM API. On success this function returns 0, otherwise it returns
92*4882a593Smuzhiyun * appropriate errno value.
93*4882a593Smuzhiyun */
fsl_upm_find(phys_addr_t addr_base,struct fsl_upm * upm)94*4882a593Smuzhiyun int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm)
95*4882a593Smuzhiyun {
96*4882a593Smuzhiyun int bank;
97*4882a593Smuzhiyun u32 br;
98*4882a593Smuzhiyun struct fsl_lbc_regs __iomem *lbc;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun bank = fsl_lbc_find(addr_base);
101*4882a593Smuzhiyun if (bank < 0)
102*4882a593Smuzhiyun return bank;
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
105*4882a593Smuzhiyun return -ENODEV;
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun lbc = fsl_lbc_ctrl_dev->regs;
108*4882a593Smuzhiyun br = in_be32(&lbc->bank[bank].br);
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun switch (br & BR_MSEL) {
111*4882a593Smuzhiyun case BR_MS_UPMA:
112*4882a593Smuzhiyun upm->mxmr = &lbc->mamr;
113*4882a593Smuzhiyun break;
114*4882a593Smuzhiyun case BR_MS_UPMB:
115*4882a593Smuzhiyun upm->mxmr = &lbc->mbmr;
116*4882a593Smuzhiyun break;
117*4882a593Smuzhiyun case BR_MS_UPMC:
118*4882a593Smuzhiyun upm->mxmr = &lbc->mcmr;
119*4882a593Smuzhiyun break;
120*4882a593Smuzhiyun default:
121*4882a593Smuzhiyun return -EINVAL;
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun switch (br & BR_PS) {
125*4882a593Smuzhiyun case BR_PS_8:
126*4882a593Smuzhiyun upm->width = 8;
127*4882a593Smuzhiyun break;
128*4882a593Smuzhiyun case BR_PS_16:
129*4882a593Smuzhiyun upm->width = 16;
130*4882a593Smuzhiyun break;
131*4882a593Smuzhiyun case BR_PS_32:
132*4882a593Smuzhiyun upm->width = 32;
133*4882a593Smuzhiyun break;
134*4882a593Smuzhiyun default:
135*4882a593Smuzhiyun return -EINVAL;
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun return 0;
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun EXPORT_SYMBOL(fsl_upm_find);
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun /**
143*4882a593Smuzhiyun * fsl_upm_run_pattern - actually run an UPM pattern
144*4882a593Smuzhiyun * @upm: pointer to the fsl_upm structure obtained via fsl_upm_find
145*4882a593Smuzhiyun * @io_base: remapped pointer to where memory access should happen
146*4882a593Smuzhiyun * @mar: MAR register content during pattern execution
147*4882a593Smuzhiyun *
148*4882a593Smuzhiyun * This function triggers dummy write to the memory specified by the io_base,
149*4882a593Smuzhiyun * thus UPM pattern actually executed. Note that mar usage depends on the
150*4882a593Smuzhiyun * pre-programmed AMX bits in the UPM RAM.
151*4882a593Smuzhiyun */
fsl_upm_run_pattern(struct fsl_upm * upm,void __iomem * io_base,u32 mar)152*4882a593Smuzhiyun int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun int ret = 0;
155*4882a593Smuzhiyun unsigned long flags;
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
158*4882a593Smuzhiyun return -ENODEV;
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun spin_lock_irqsave(&fsl_lbc_lock, flags);
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun out_be32(&fsl_lbc_ctrl_dev->regs->mar, mar);
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun switch (upm->width) {
165*4882a593Smuzhiyun case 8:
166*4882a593Smuzhiyun out_8(io_base, 0x0);
167*4882a593Smuzhiyun break;
168*4882a593Smuzhiyun case 16:
169*4882a593Smuzhiyun out_be16(io_base, 0x0);
170*4882a593Smuzhiyun break;
171*4882a593Smuzhiyun case 32:
172*4882a593Smuzhiyun out_be32(io_base, 0x0);
173*4882a593Smuzhiyun break;
174*4882a593Smuzhiyun default:
175*4882a593Smuzhiyun ret = -EINVAL;
176*4882a593Smuzhiyun break;
177*4882a593Smuzhiyun }
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun spin_unlock_irqrestore(&fsl_lbc_lock, flags);
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun return ret;
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun EXPORT_SYMBOL(fsl_upm_run_pattern);
184*4882a593Smuzhiyun
fsl_lbc_ctrl_init(struct fsl_lbc_ctrl * ctrl,struct device_node * node)185*4882a593Smuzhiyun static int fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl,
186*4882a593Smuzhiyun struct device_node *node)
187*4882a593Smuzhiyun {
188*4882a593Smuzhiyun struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun /* clear event registers */
191*4882a593Smuzhiyun setbits32(&lbc->ltesr, LTESR_CLEAR);
192*4882a593Smuzhiyun out_be32(&lbc->lteatr, 0);
193*4882a593Smuzhiyun out_be32(&lbc->ltear, 0);
194*4882a593Smuzhiyun out_be32(&lbc->lteccr, LTECCR_CLEAR);
195*4882a593Smuzhiyun out_be32(&lbc->ltedr, LTEDR_ENABLE);
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun /* Set the monitor timeout value to the maximum for erratum A001 */
198*4882a593Smuzhiyun if (of_device_is_compatible(node, "fsl,elbc"))
199*4882a593Smuzhiyun clrsetbits_be32(&lbc->lbcr, LBCR_BMT, LBCR_BMTPS);
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun return 0;
202*4882a593Smuzhiyun }
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun /*
205*4882a593Smuzhiyun * NOTE: This interrupt is used to report localbus events of various kinds,
206*4882a593Smuzhiyun * such as transaction errors on the chipselects.
207*4882a593Smuzhiyun */
208*4882a593Smuzhiyun
fsl_lbc_ctrl_irq(int irqno,void * data)209*4882a593Smuzhiyun static irqreturn_t fsl_lbc_ctrl_irq(int irqno, void *data)
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun struct fsl_lbc_ctrl *ctrl = data;
212*4882a593Smuzhiyun struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
213*4882a593Smuzhiyun u32 status;
214*4882a593Smuzhiyun unsigned long flags;
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun spin_lock_irqsave(&fsl_lbc_lock, flags);
217*4882a593Smuzhiyun status = in_be32(&lbc->ltesr);
218*4882a593Smuzhiyun if (!status) {
219*4882a593Smuzhiyun spin_unlock_irqrestore(&fsl_lbc_lock, flags);
220*4882a593Smuzhiyun return IRQ_NONE;
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun out_be32(&lbc->ltesr, LTESR_CLEAR);
224*4882a593Smuzhiyun out_be32(&lbc->lteatr, 0);
225*4882a593Smuzhiyun out_be32(&lbc->ltear, 0);
226*4882a593Smuzhiyun ctrl->irq_status = status;
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun if (status & LTESR_BM)
229*4882a593Smuzhiyun dev_err(ctrl->dev, "Local bus monitor time-out: "
230*4882a593Smuzhiyun "LTESR 0x%08X\n", status);
231*4882a593Smuzhiyun if (status & LTESR_WP)
232*4882a593Smuzhiyun dev_err(ctrl->dev, "Write protect error: "
233*4882a593Smuzhiyun "LTESR 0x%08X\n", status);
234*4882a593Smuzhiyun if (status & LTESR_ATMW)
235*4882a593Smuzhiyun dev_err(ctrl->dev, "Atomic write error: "
236*4882a593Smuzhiyun "LTESR 0x%08X\n", status);
237*4882a593Smuzhiyun if (status & LTESR_ATMR)
238*4882a593Smuzhiyun dev_err(ctrl->dev, "Atomic read error: "
239*4882a593Smuzhiyun "LTESR 0x%08X\n", status);
240*4882a593Smuzhiyun if (status & LTESR_CS)
241*4882a593Smuzhiyun dev_err(ctrl->dev, "Chip select error: "
242*4882a593Smuzhiyun "LTESR 0x%08X\n", status);
243*4882a593Smuzhiyun if (status & LTESR_FCT) {
244*4882a593Smuzhiyun dev_err(ctrl->dev, "FCM command time-out: "
245*4882a593Smuzhiyun "LTESR 0x%08X\n", status);
246*4882a593Smuzhiyun smp_wmb();
247*4882a593Smuzhiyun wake_up(&ctrl->irq_wait);
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun if (status & LTESR_PAR) {
250*4882a593Smuzhiyun dev_err(ctrl->dev, "Parity or Uncorrectable ECC error: "
251*4882a593Smuzhiyun "LTESR 0x%08X\n", status);
252*4882a593Smuzhiyun smp_wmb();
253*4882a593Smuzhiyun wake_up(&ctrl->irq_wait);
254*4882a593Smuzhiyun }
255*4882a593Smuzhiyun if (status & LTESR_CC) {
256*4882a593Smuzhiyun smp_wmb();
257*4882a593Smuzhiyun wake_up(&ctrl->irq_wait);
258*4882a593Smuzhiyun }
259*4882a593Smuzhiyun if (status & ~LTESR_MASK)
260*4882a593Smuzhiyun dev_err(ctrl->dev, "Unknown error: "
261*4882a593Smuzhiyun "LTESR 0x%08X\n", status);
262*4882a593Smuzhiyun spin_unlock_irqrestore(&fsl_lbc_lock, flags);
263*4882a593Smuzhiyun return IRQ_HANDLED;
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun /*
267*4882a593Smuzhiyun * fsl_lbc_ctrl_probe
268*4882a593Smuzhiyun *
269*4882a593Smuzhiyun * called by device layer when it finds a device matching
270*4882a593Smuzhiyun * one our driver can handled. This code allocates all of
271*4882a593Smuzhiyun * the resources needed for the controller only. The
272*4882a593Smuzhiyun * resources for the NAND banks themselves are allocated
273*4882a593Smuzhiyun * in the chip probe function.
274*4882a593Smuzhiyun */
275*4882a593Smuzhiyun
fsl_lbc_ctrl_probe(struct platform_device * dev)276*4882a593Smuzhiyun static int fsl_lbc_ctrl_probe(struct platform_device *dev)
277*4882a593Smuzhiyun {
278*4882a593Smuzhiyun int ret;
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun if (!dev->dev.of_node) {
281*4882a593Smuzhiyun dev_err(&dev->dev, "Device OF-Node is NULL");
282*4882a593Smuzhiyun return -EFAULT;
283*4882a593Smuzhiyun }
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun fsl_lbc_ctrl_dev = kzalloc(sizeof(*fsl_lbc_ctrl_dev), GFP_KERNEL);
286*4882a593Smuzhiyun if (!fsl_lbc_ctrl_dev)
287*4882a593Smuzhiyun return -ENOMEM;
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun dev_set_drvdata(&dev->dev, fsl_lbc_ctrl_dev);
290*4882a593Smuzhiyun
291*4882a593Smuzhiyun spin_lock_init(&fsl_lbc_ctrl_dev->lock);
292*4882a593Smuzhiyun init_waitqueue_head(&fsl_lbc_ctrl_dev->irq_wait);
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun fsl_lbc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0);
295*4882a593Smuzhiyun if (!fsl_lbc_ctrl_dev->regs) {
296*4882a593Smuzhiyun dev_err(&dev->dev, "failed to get memory region\n");
297*4882a593Smuzhiyun ret = -ENODEV;
298*4882a593Smuzhiyun goto err;
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun fsl_lbc_ctrl_dev->irq[0] = irq_of_parse_and_map(dev->dev.of_node, 0);
302*4882a593Smuzhiyun if (!fsl_lbc_ctrl_dev->irq[0]) {
303*4882a593Smuzhiyun dev_err(&dev->dev, "failed to get irq resource\n");
304*4882a593Smuzhiyun ret = -ENODEV;
305*4882a593Smuzhiyun goto err;
306*4882a593Smuzhiyun }
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun fsl_lbc_ctrl_dev->dev = &dev->dev;
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun ret = fsl_lbc_ctrl_init(fsl_lbc_ctrl_dev, dev->dev.of_node);
311*4882a593Smuzhiyun if (ret < 0)
312*4882a593Smuzhiyun goto err;
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun ret = request_irq(fsl_lbc_ctrl_dev->irq[0], fsl_lbc_ctrl_irq, 0,
315*4882a593Smuzhiyun "fsl-lbc", fsl_lbc_ctrl_dev);
316*4882a593Smuzhiyun if (ret != 0) {
317*4882a593Smuzhiyun dev_err(&dev->dev, "failed to install irq (%d)\n",
318*4882a593Smuzhiyun fsl_lbc_ctrl_dev->irq[0]);
319*4882a593Smuzhiyun ret = fsl_lbc_ctrl_dev->irq[0];
320*4882a593Smuzhiyun goto err;
321*4882a593Smuzhiyun }
322*4882a593Smuzhiyun
323*4882a593Smuzhiyun fsl_lbc_ctrl_dev->irq[1] = irq_of_parse_and_map(dev->dev.of_node, 1);
324*4882a593Smuzhiyun if (fsl_lbc_ctrl_dev->irq[1]) {
325*4882a593Smuzhiyun ret = request_irq(fsl_lbc_ctrl_dev->irq[1], fsl_lbc_ctrl_irq,
326*4882a593Smuzhiyun IRQF_SHARED, "fsl-lbc-err", fsl_lbc_ctrl_dev);
327*4882a593Smuzhiyun if (ret) {
328*4882a593Smuzhiyun dev_err(&dev->dev, "failed to install irq (%d)\n",
329*4882a593Smuzhiyun fsl_lbc_ctrl_dev->irq[1]);
330*4882a593Smuzhiyun ret = fsl_lbc_ctrl_dev->irq[1];
331*4882a593Smuzhiyun goto err1;
332*4882a593Smuzhiyun }
333*4882a593Smuzhiyun }
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun /* Enable interrupts for any detected events */
336*4882a593Smuzhiyun out_be32(&fsl_lbc_ctrl_dev->regs->lteir, LTEIR_ENABLE);
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun return 0;
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun err1:
341*4882a593Smuzhiyun free_irq(fsl_lbc_ctrl_dev->irq[0], fsl_lbc_ctrl_dev);
342*4882a593Smuzhiyun err:
343*4882a593Smuzhiyun iounmap(fsl_lbc_ctrl_dev->regs);
344*4882a593Smuzhiyun kfree(fsl_lbc_ctrl_dev);
345*4882a593Smuzhiyun fsl_lbc_ctrl_dev = NULL;
346*4882a593Smuzhiyun return ret;
347*4882a593Smuzhiyun }
348*4882a593Smuzhiyun
349*4882a593Smuzhiyun #ifdef CONFIG_SUSPEND
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun /* save lbc registers */
fsl_lbc_syscore_suspend(void)352*4882a593Smuzhiyun static int fsl_lbc_syscore_suspend(void)
353*4882a593Smuzhiyun {
354*4882a593Smuzhiyun struct fsl_lbc_ctrl *ctrl;
355*4882a593Smuzhiyun struct fsl_lbc_regs __iomem *lbc;
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun ctrl = fsl_lbc_ctrl_dev;
358*4882a593Smuzhiyun if (!ctrl)
359*4882a593Smuzhiyun goto out;
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun lbc = ctrl->regs;
362*4882a593Smuzhiyun if (!lbc)
363*4882a593Smuzhiyun goto out;
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun ctrl->saved_regs = kmalloc(sizeof(struct fsl_lbc_regs), GFP_KERNEL);
366*4882a593Smuzhiyun if (!ctrl->saved_regs)
367*4882a593Smuzhiyun return -ENOMEM;
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun _memcpy_fromio(ctrl->saved_regs, lbc, sizeof(struct fsl_lbc_regs));
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun out:
372*4882a593Smuzhiyun return 0;
373*4882a593Smuzhiyun }
374*4882a593Smuzhiyun
375*4882a593Smuzhiyun /* restore lbc registers */
fsl_lbc_syscore_resume(void)376*4882a593Smuzhiyun static void fsl_lbc_syscore_resume(void)
377*4882a593Smuzhiyun {
378*4882a593Smuzhiyun struct fsl_lbc_ctrl *ctrl;
379*4882a593Smuzhiyun struct fsl_lbc_regs __iomem *lbc;
380*4882a593Smuzhiyun
381*4882a593Smuzhiyun ctrl = fsl_lbc_ctrl_dev;
382*4882a593Smuzhiyun if (!ctrl)
383*4882a593Smuzhiyun goto out;
384*4882a593Smuzhiyun
385*4882a593Smuzhiyun lbc = ctrl->regs;
386*4882a593Smuzhiyun if (!lbc)
387*4882a593Smuzhiyun goto out;
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun if (ctrl->saved_regs) {
390*4882a593Smuzhiyun _memcpy_toio(lbc, ctrl->saved_regs,
391*4882a593Smuzhiyun sizeof(struct fsl_lbc_regs));
392*4882a593Smuzhiyun kfree(ctrl->saved_regs);
393*4882a593Smuzhiyun ctrl->saved_regs = NULL;
394*4882a593Smuzhiyun }
395*4882a593Smuzhiyun
396*4882a593Smuzhiyun out:
397*4882a593Smuzhiyun return;
398*4882a593Smuzhiyun }
399*4882a593Smuzhiyun #endif /* CONFIG_SUSPEND */
400*4882a593Smuzhiyun
401*4882a593Smuzhiyun static const struct of_device_id fsl_lbc_match[] = {
402*4882a593Smuzhiyun { .compatible = "fsl,elbc", },
403*4882a593Smuzhiyun { .compatible = "fsl,pq3-localbus", },
404*4882a593Smuzhiyun { .compatible = "fsl,pq2-localbus", },
405*4882a593Smuzhiyun { .compatible = "fsl,pq2pro-localbus", },
406*4882a593Smuzhiyun {},
407*4882a593Smuzhiyun };
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun #ifdef CONFIG_SUSPEND
410*4882a593Smuzhiyun static struct syscore_ops lbc_syscore_pm_ops = {
411*4882a593Smuzhiyun .suspend = fsl_lbc_syscore_suspend,
412*4882a593Smuzhiyun .resume = fsl_lbc_syscore_resume,
413*4882a593Smuzhiyun };
414*4882a593Smuzhiyun #endif
415*4882a593Smuzhiyun
416*4882a593Smuzhiyun static struct platform_driver fsl_lbc_ctrl_driver = {
417*4882a593Smuzhiyun .driver = {
418*4882a593Smuzhiyun .name = "fsl-lbc",
419*4882a593Smuzhiyun .of_match_table = fsl_lbc_match,
420*4882a593Smuzhiyun },
421*4882a593Smuzhiyun .probe = fsl_lbc_ctrl_probe,
422*4882a593Smuzhiyun };
423*4882a593Smuzhiyun
fsl_lbc_init(void)424*4882a593Smuzhiyun static int __init fsl_lbc_init(void)
425*4882a593Smuzhiyun {
426*4882a593Smuzhiyun #ifdef CONFIG_SUSPEND
427*4882a593Smuzhiyun register_syscore_ops(&lbc_syscore_pm_ops);
428*4882a593Smuzhiyun #endif
429*4882a593Smuzhiyun return platform_driver_register(&fsl_lbc_ctrl_driver);
430*4882a593Smuzhiyun }
431*4882a593Smuzhiyun subsys_initcall(fsl_lbc_init);
432