xref: /openbmc/linux/arch/arm/mach-sa1100/neponset.c (revision 056c0acf)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * linux/arch/arm/mach-sa1100/neponset.c
31da177e4SLinus Torvalds  */
49590e898SRussell King #include <linux/err.h>
51da177e4SLinus Torvalds #include <linux/init.h>
61da177e4SLinus Torvalds #include <linux/ioport.h>
7ced8d21cSRussell King #include <linux/irq.h>
892e617d9SRussell King #include <linux/kernel.h>
9ae14c2e2SRussell King #include <linux/module.h>
106920b5a7SRussell King #include <linux/platform_data/sa11x0-serial.h>
11d052d1beSRussell King #include <linux/platform_device.h>
1251f93390SRussell King #include <linux/pm.h>
1392e617d9SRussell King #include <linux/serial_core.h>
14ae14c2e2SRussell King #include <linux/slab.h>
15b70661c7SArnd Bergmann #include <linux/smc91x.h>
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds #include <asm/mach-types.h>
181da177e4SLinus Torvalds #include <asm/mach/map.h>
191da177e4SLinus Torvalds #include <asm/hardware/sa1111.h>
201da177e4SLinus Torvalds #include <asm/sizes.h>
211da177e4SLinus Torvalds 
2292e617d9SRussell King #include <mach/hardware.h>
2392e617d9SRussell King #include <mach/assabet.h>
2492e617d9SRussell King #include <mach/neponset.h>
25f314f33bSRob Herring #include <mach/irqs.h>
2692e617d9SRussell King 
27ced8d21cSRussell King #define NEP_IRQ_SMC91X	0
28ced8d21cSRussell King #define NEP_IRQ_USAR	1
29ced8d21cSRussell King #define NEP_IRQ_SA1111	2
30ced8d21cSRussell King #define NEP_IRQ_NR	3
31ced8d21cSRussell King 
32f942b0fdSRussell King #define WHOAMI		0x00
33f942b0fdSRussell King #define LEDS		0x10
34f942b0fdSRussell King #define SWPK		0x20
35f942b0fdSRussell King #define IRR		0x24
36f942b0fdSRussell King #define KP_Y_IN		0x80
37f942b0fdSRussell King #define KP_X_OUT	0x90
38f942b0fdSRussell King #define NCR_0		0xa0
39f942b0fdSRussell King #define MDM_CTL_0	0xb0
40f942b0fdSRussell King #define MDM_CTL_1	0xb4
41f942b0fdSRussell King #define AUD_CTL		0xc0
42f942b0fdSRussell King 
43f942b0fdSRussell King #define IRR_ETHERNET	(1 << 0)
44f942b0fdSRussell King #define IRR_USAR	(1 << 1)
45f942b0fdSRussell King #define IRR_SA1111	(1 << 2)
46f942b0fdSRussell King 
47f942b0fdSRussell King #define MDM_CTL0_RTS1	(1 << 0)
48f942b0fdSRussell King #define MDM_CTL0_DTR1	(1 << 1)
49f942b0fdSRussell King #define MDM_CTL0_RTS2	(1 << 2)
50f942b0fdSRussell King #define MDM_CTL0_DTR2	(1 << 3)
51f942b0fdSRussell King 
52f942b0fdSRussell King #define MDM_CTL1_CTS1	(1 << 0)
53f942b0fdSRussell King #define MDM_CTL1_DSR1	(1 << 1)
54f942b0fdSRussell King #define MDM_CTL1_DCD1	(1 << 2)
55f942b0fdSRussell King #define MDM_CTL1_CTS2	(1 << 3)
56f942b0fdSRussell King #define MDM_CTL1_DSR2	(1 << 4)
57f942b0fdSRussell King #define MDM_CTL1_DCD2	(1 << 5)
58f942b0fdSRussell King 
59f942b0fdSRussell King #define AUD_SEL_1341	(1 << 0)
60f942b0fdSRussell King #define AUD_MUTE_1341	(1 << 1)
61f942b0fdSRussell King 
62bab50a35SRussell King extern void sa1110_mb_disable(void);
63bab50a35SRussell King 
64ae14c2e2SRussell King struct neponset_drvdata {
65f942b0fdSRussell King 	void __iomem *base;
669590e898SRussell King 	struct platform_device *sa1111;
679590e898SRussell King 	struct platform_device *smc91x;
68ced8d21cSRussell King 	unsigned irq_base;
69ae14c2e2SRussell King #ifdef CONFIG_PM_SLEEP
70ae14c2e2SRussell King 	u32 ncr0;
71ae14c2e2SRussell King 	u32 mdm_ctl_0;
72ae14c2e2SRussell King #endif
73ae14c2e2SRussell King };
74ae14c2e2SRussell King 
75f942b0fdSRussell King static void __iomem *nep_base;
76f942b0fdSRussell King 
776ad1b614SRussell King void neponset_ncr_frob(unsigned int mask, unsigned int val)
786ad1b614SRussell King {
79f942b0fdSRussell King 	void __iomem *base = nep_base;
80f942b0fdSRussell King 
81f942b0fdSRussell King 	if (base) {
826ad1b614SRussell King 		unsigned long flags;
83f942b0fdSRussell King 		unsigned v;
846ad1b614SRussell King 
856ad1b614SRussell King 		local_irq_save(flags);
86f942b0fdSRussell King 		v = readb_relaxed(base + NCR_0);
87f942b0fdSRussell King 		writeb_relaxed((v & ~mask) | val, base + NCR_0);
886ad1b614SRussell King 		local_irq_restore(flags);
89f942b0fdSRussell King 	} else {
90f942b0fdSRussell King 		WARN(1, "nep_base unset\n");
91f942b0fdSRussell King 	}
926ad1b614SRussell King }
93ef0c1484SRussell King EXPORT_SYMBOL(neponset_ncr_frob);
946ad1b614SRussell King 
951da177e4SLinus Torvalds static void neponset_set_mctrl(struct uart_port *port, u_int mctrl)
961da177e4SLinus Torvalds {
97f942b0fdSRussell King 	void __iomem *base = nep_base;
98f942b0fdSRussell King 	u_int mdm_ctl0;
991da177e4SLinus Torvalds 
100f942b0fdSRussell King 	if (!base)
101f942b0fdSRussell King 		return;
102f942b0fdSRussell King 
103f942b0fdSRussell King 	mdm_ctl0 = readb_relaxed(base + MDM_CTL_0);
1041da177e4SLinus Torvalds 	if (port->mapbase == _Ser1UTCR0) {
1051da177e4SLinus Torvalds 		if (mctrl & TIOCM_RTS)
1061da177e4SLinus Torvalds 			mdm_ctl0 &= ~MDM_CTL0_RTS2;
1071da177e4SLinus Torvalds 		else
1081da177e4SLinus Torvalds 			mdm_ctl0 |= MDM_CTL0_RTS2;
1091da177e4SLinus Torvalds 
1101da177e4SLinus Torvalds 		if (mctrl & TIOCM_DTR)
1111da177e4SLinus Torvalds 			mdm_ctl0 &= ~MDM_CTL0_DTR2;
1121da177e4SLinus Torvalds 		else
1131da177e4SLinus Torvalds 			mdm_ctl0 |= MDM_CTL0_DTR2;
1141da177e4SLinus Torvalds 	} else if (port->mapbase == _Ser3UTCR0) {
1151da177e4SLinus Torvalds 		if (mctrl & TIOCM_RTS)
1161da177e4SLinus Torvalds 			mdm_ctl0 &= ~MDM_CTL0_RTS1;
1171da177e4SLinus Torvalds 		else
1181da177e4SLinus Torvalds 			mdm_ctl0 |= MDM_CTL0_RTS1;
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds 		if (mctrl & TIOCM_DTR)
1211da177e4SLinus Torvalds 			mdm_ctl0 &= ~MDM_CTL0_DTR1;
1221da177e4SLinus Torvalds 		else
1231da177e4SLinus Torvalds 			mdm_ctl0 |= MDM_CTL0_DTR1;
1241da177e4SLinus Torvalds 	}
1251da177e4SLinus Torvalds 
126f942b0fdSRussell King 	writeb_relaxed(mdm_ctl0, base + MDM_CTL_0);
1271da177e4SLinus Torvalds }
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds static u_int neponset_get_mctrl(struct uart_port *port)
1301da177e4SLinus Torvalds {
131f942b0fdSRussell King 	void __iomem *base = nep_base;
1321da177e4SLinus Torvalds 	u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR;
133f942b0fdSRussell King 	u_int mdm_ctl1;
1341da177e4SLinus Torvalds 
135f942b0fdSRussell King 	if (!base)
136f942b0fdSRussell King 		return ret;
137f942b0fdSRussell King 
138f942b0fdSRussell King 	mdm_ctl1 = readb_relaxed(base + MDM_CTL_1);
1391da177e4SLinus Torvalds 	if (port->mapbase == _Ser1UTCR0) {
1401da177e4SLinus Torvalds 		if (mdm_ctl1 & MDM_CTL1_DCD2)
1411da177e4SLinus Torvalds 			ret &= ~TIOCM_CD;
1421da177e4SLinus Torvalds 		if (mdm_ctl1 & MDM_CTL1_CTS2)
1431da177e4SLinus Torvalds 			ret &= ~TIOCM_CTS;
1441da177e4SLinus Torvalds 		if (mdm_ctl1 & MDM_CTL1_DSR2)
1451da177e4SLinus Torvalds 			ret &= ~TIOCM_DSR;
1461da177e4SLinus Torvalds 	} else if (port->mapbase == _Ser3UTCR0) {
1471da177e4SLinus Torvalds 		if (mdm_ctl1 & MDM_CTL1_DCD1)
1481da177e4SLinus Torvalds 			ret &= ~TIOCM_CD;
1491da177e4SLinus Torvalds 		if (mdm_ctl1 & MDM_CTL1_CTS1)
1501da177e4SLinus Torvalds 			ret &= ~TIOCM_CTS;
1511da177e4SLinus Torvalds 		if (mdm_ctl1 & MDM_CTL1_DSR1)
1521da177e4SLinus Torvalds 			ret &= ~TIOCM_DSR;
1531da177e4SLinus Torvalds 	}
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds 	return ret;
1561da177e4SLinus Torvalds }
1571da177e4SLinus Torvalds 
158351a102dSGreg Kroah-Hartman static struct sa1100_port_fns neponset_port_fns = {
1591da177e4SLinus Torvalds 	.set_mctrl	= neponset_set_mctrl,
1601da177e4SLinus Torvalds 	.get_mctrl	= neponset_get_mctrl,
1611da177e4SLinus Torvalds };
1621da177e4SLinus Torvalds 
16371045520SRussell King /*
16492e617d9SRussell King  * Install handler for Neponset IRQ.  Note that we have to loop here
16592e617d9SRussell King  * since the ETHERNET and USAR IRQs are level based, and we need to
16692e617d9SRussell King  * ensure that the IRQ signal is deasserted before returning.  This
16792e617d9SRussell King  * is rather unfortunate.
16892e617d9SRussell King  */
169ced8d21cSRussell King static void neponset_irq_handler(unsigned int irq, struct irq_desc *desc)
17092e617d9SRussell King {
171ced8d21cSRussell King 	struct neponset_drvdata *d = irq_desc_get_handler_data(desc);
17292e617d9SRussell King 	unsigned int irr;
17392e617d9SRussell King 
17492e617d9SRussell King 	while (1) {
17592e617d9SRussell King 		/*
17692e617d9SRussell King 		 * Acknowledge the parent IRQ.
17792e617d9SRussell King 		 */
17892e617d9SRussell King 		desc->irq_data.chip->irq_ack(&desc->irq_data);
17992e617d9SRussell King 
18092e617d9SRussell King 		/*
18192e617d9SRussell King 		 * Read the interrupt reason register.  Let's have all
18292e617d9SRussell King 		 * active IRQ bits high.  Note: there is a typo in the
18392e617d9SRussell King 		 * Neponset user's guide for the SA1111 IRR level.
18492e617d9SRussell King 		 */
185f942b0fdSRussell King 		irr = readb_relaxed(d->base + IRR);
186f942b0fdSRussell King 		irr ^= IRR_ETHERNET | IRR_USAR;
18792e617d9SRussell King 
18892e617d9SRussell King 		if ((irr & (IRR_ETHERNET | IRR_USAR | IRR_SA1111)) == 0)
18992e617d9SRussell King 			break;
19092e617d9SRussell King 
19192e617d9SRussell King 		/*
19292e617d9SRussell King 		 * Since there is no individual mask, we have to
19392e617d9SRussell King 		 * mask the parent IRQ.  This is safe, since we'll
19492e617d9SRussell King 		 * recheck the register for any pending IRQs.
19592e617d9SRussell King 		 */
19692e617d9SRussell King 		if (irr & (IRR_ETHERNET | IRR_USAR)) {
19792e617d9SRussell King 			desc->irq_data.chip->irq_mask(&desc->irq_data);
19892e617d9SRussell King 
19992e617d9SRussell King 			/*
20092e617d9SRussell King 			 * Ack the interrupt now to prevent re-entering
20192e617d9SRussell King 			 * this neponset handler.  Again, this is safe
20292e617d9SRussell King 			 * since we'll check the IRR register prior to
20392e617d9SRussell King 			 * leaving.
20492e617d9SRussell King 			 */
20592e617d9SRussell King 			desc->irq_data.chip->irq_ack(&desc->irq_data);
20692e617d9SRussell King 
207ced8d21cSRussell King 			if (irr & IRR_ETHERNET)
208ced8d21cSRussell King 				generic_handle_irq(d->irq_base + NEP_IRQ_SMC91X);
20992e617d9SRussell King 
210ced8d21cSRussell King 			if (irr & IRR_USAR)
211ced8d21cSRussell King 				generic_handle_irq(d->irq_base + NEP_IRQ_USAR);
21292e617d9SRussell King 
21392e617d9SRussell King 			desc->irq_data.chip->irq_unmask(&desc->irq_data);
21492e617d9SRussell King 		}
21592e617d9SRussell King 
216ced8d21cSRussell King 		if (irr & IRR_SA1111)
217ced8d21cSRussell King 			generic_handle_irq(d->irq_base + NEP_IRQ_SA1111);
21892e617d9SRussell King 	}
21992e617d9SRussell King }
22092e617d9SRussell King 
221ced8d21cSRussell King /* Yes, we really do not have any kind of masking or unmasking */
22271045520SRussell King static void nochip_noop(struct irq_data *irq)
22371045520SRussell King {
22471045520SRussell King }
22571045520SRussell King 
22671045520SRussell King static struct irq_chip nochip = {
22771045520SRussell King 	.name = "neponset",
22871045520SRussell King 	.irq_ack = nochip_noop,
22971045520SRussell King 	.irq_mask = nochip_noop,
23071045520SRussell King 	.irq_unmask = nochip_noop,
23171045520SRussell King };
23271045520SRussell King 
23392e617d9SRussell King static struct sa1111_platform_data sa1111_info = {
23407be45f5SRussell King 	.disable_devs	= SA1111_DEVID_PS2_MSE,
23592e617d9SRussell King };
23692e617d9SRussell King 
237351a102dSGreg Kroah-Hartman static int neponset_probe(struct platform_device *dev)
2381da177e4SLinus Torvalds {
239ae14c2e2SRussell King 	struct neponset_drvdata *d;
240f942b0fdSRussell King 	struct resource *nep_res, *sa1111_res, *smc91x_res;
241ced8d21cSRussell King 	struct resource sa1111_resources[] = {
242ced8d21cSRussell King 		DEFINE_RES_MEM(0x40000000, SZ_8K),
243ced8d21cSRussell King 		{ .flags = IORESOURCE_IRQ },
244ced8d21cSRussell King 	};
2459590e898SRussell King 	struct platform_device_info sa1111_devinfo = {
2469590e898SRussell King 		.parent = &dev->dev,
2479590e898SRussell King 		.name = "sa1111",
2489590e898SRussell King 		.id = 0,
2499590e898SRussell King 		.res = sa1111_resources,
2509590e898SRussell King 		.num_res = ARRAY_SIZE(sa1111_resources),
2519590e898SRussell King 		.data = &sa1111_info,
2529590e898SRussell King 		.size_data = sizeof(sa1111_info),
2539590e898SRussell King 		.dma_mask = 0xffffffffUL,
2549590e898SRussell King 	};
255ced8d21cSRussell King 	struct resource smc91x_resources[] = {
256ced8d21cSRussell King 		DEFINE_RES_MEM_NAMED(SA1100_CS3_PHYS,
257ced8d21cSRussell King 			0x02000000, "smc91x-regs"),
258ced8d21cSRussell King 		DEFINE_RES_MEM_NAMED(SA1100_CS3_PHYS + 0x02000000,
259ced8d21cSRussell King 			0x02000000, "smc91x-attrib"),
260ced8d21cSRussell King 		{ .flags = IORESOURCE_IRQ },
261ced8d21cSRussell King 	};
262b70661c7SArnd Bergmann 	struct smc91x_platdata smc91x_platdata = {
263b70661c7SArnd Bergmann 		.flags = SMC91X_USE_8BIT | SMC91X_IO_SHIFT_2 | SMC91X_NOWAIT,
264b70661c7SArnd Bergmann 	};
2659590e898SRussell King 	struct platform_device_info smc91x_devinfo = {
2669590e898SRussell King 		.parent = &dev->dev,
2679590e898SRussell King 		.name = "smc91x",
2689590e898SRussell King 		.id = 0,
2699590e898SRussell King 		.res = smc91x_resources,
2709590e898SRussell King 		.num_res = ARRAY_SIZE(smc91x_resources),
27104b91701SArnd Bergmann 		.data = &smc91x_platdata,
27204b91701SArnd Bergmann 		.size_data = sizeof(smc91x_platdata),
2739590e898SRussell King 	};
274b6bdfcf5SRussell King 	int ret, irq;
275b6bdfcf5SRussell King 
276f942b0fdSRussell King 	if (nep_base)
277f942b0fdSRussell King 		return -EBUSY;
278f942b0fdSRussell King 
279b6bdfcf5SRussell King 	irq = ret = platform_get_irq(dev, 0);
280b6bdfcf5SRussell King 	if (ret < 0)
281b6bdfcf5SRussell King 		goto err_alloc;
282ae14c2e2SRussell King 
283f942b0fdSRussell King 	nep_res = platform_get_resource(dev, IORESOURCE_MEM, 0);
284d2e539a5SRussell King 	smc91x_res = platform_get_resource(dev, IORESOURCE_MEM, 1);
285d2e539a5SRussell King 	sa1111_res = platform_get_resource(dev, IORESOURCE_MEM, 2);
286f942b0fdSRussell King 	if (!nep_res || !smc91x_res || !sa1111_res) {
287d2e539a5SRussell King 		ret = -ENXIO;
288d2e539a5SRussell King 		goto err_alloc;
289d2e539a5SRussell King 	}
290d2e539a5SRussell King 
291ae14c2e2SRussell King 	d = kzalloc(sizeof(*d), GFP_KERNEL);
292ae14c2e2SRussell King 	if (!d) {
293ae14c2e2SRussell King 		ret = -ENOMEM;
294ae14c2e2SRussell King 		goto err_alloc;
295ae14c2e2SRussell King 	}
296ae14c2e2SRussell King 
297f942b0fdSRussell King 	d->base = ioremap(nep_res->start, SZ_4K);
298f942b0fdSRussell King 	if (!d->base) {
299f942b0fdSRussell King 		ret = -ENOMEM;
300f942b0fdSRussell King 		goto err_ioremap;
301f942b0fdSRussell King 	}
302f942b0fdSRussell King 
303f942b0fdSRussell King 	if (readb_relaxed(d->base + WHOAMI) != 0x11) {
304f942b0fdSRussell King 		dev_warn(&dev->dev, "Neponset board detected, but wrong ID: %02x\n",
305f942b0fdSRussell King 			 readb_relaxed(d->base + WHOAMI));
306f942b0fdSRussell King 		ret = -ENODEV;
307f942b0fdSRussell King 		goto err_id;
308f942b0fdSRussell King 	}
309f942b0fdSRussell King 
310ced8d21cSRussell King 	ret = irq_alloc_descs(-1, IRQ_BOARD_START, NEP_IRQ_NR, -1);
311ced8d21cSRussell King 	if (ret <= 0) {
312ced8d21cSRussell King 		dev_err(&dev->dev, "unable to allocate %u irqs: %d\n",
313ced8d21cSRussell King 			NEP_IRQ_NR, ret);
314ced8d21cSRussell King 		if (ret == 0)
315ced8d21cSRussell King 			ret = -ENOMEM;
316ced8d21cSRussell King 		goto err_irq_alloc;
317ced8d21cSRussell King 	}
3181da177e4SLinus Torvalds 
319ced8d21cSRussell King 	d->irq_base = ret;
320ced8d21cSRussell King 
321ced8d21cSRussell King 	irq_set_chip_and_handler(d->irq_base + NEP_IRQ_SMC91X, &nochip,
322ced8d21cSRussell King 		handle_simple_irq);
323ced8d21cSRussell King 	set_irq_flags(d->irq_base + NEP_IRQ_SMC91X, IRQF_VALID | IRQF_PROBE);
324ced8d21cSRussell King 	irq_set_chip_and_handler(d->irq_base + NEP_IRQ_USAR, &nochip,
325ced8d21cSRussell King 		handle_simple_irq);
326ced8d21cSRussell King 	set_irq_flags(d->irq_base + NEP_IRQ_USAR, IRQF_VALID | IRQF_PROBE);
327ced8d21cSRussell King 	irq_set_chip(d->irq_base + NEP_IRQ_SA1111, &nochip);
328ced8d21cSRussell King 
329b6bdfcf5SRussell King 	irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
330056c0acfSRussell King 	irq_set_chained_handler_and_data(irq, neponset_irq_handler, d);
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds 	/*
333ced8d21cSRussell King 	 * We would set IRQ_GPIO25 to be a wake-up IRQ, but unfortunately
334ced8d21cSRussell King 	 * something on the Neponset activates this IRQ on sleep (eth?)
3351da177e4SLinus Torvalds 	 */
3361da177e4SLinus Torvalds #if 0
337b6bdfcf5SRussell King 	enable_irq_wake(irq);
3381da177e4SLinus Torvalds #endif
3391da177e4SLinus Torvalds 
340ced8d21cSRussell King 	dev_info(&dev->dev, "Neponset daughter board, providing IRQ%u-%u\n",
341ced8d21cSRussell King 		 d->irq_base, d->irq_base + NEP_IRQ_NR - 1);
342f942b0fdSRussell King 	nep_base = d->base;
3431da177e4SLinus Torvalds 
344ced8d21cSRussell King 	sa1100_register_uart_fns(&neponset_port_fns);
345ced8d21cSRussell King 
346bab50a35SRussell King 	/* Ensure that the memory bus request/grant signals are setup */
347bab50a35SRussell King 	sa1110_mb_disable();
348bab50a35SRussell King 
349ced8d21cSRussell King 	/* Disable GPIO 0/1 drivers so the buttons work on the Assabet */
350f942b0fdSRussell King 	writeb_relaxed(NCR_GP01_OFF, d->base + NCR_0);
3511da177e4SLinus Torvalds 
352d2e539a5SRussell King 	sa1111_resources[0].parent = sa1111_res;
353ced8d21cSRussell King 	sa1111_resources[1].start = d->irq_base + NEP_IRQ_SA1111;
354ced8d21cSRussell King 	sa1111_resources[1].end = d->irq_base + NEP_IRQ_SA1111;
3559590e898SRussell King 	d->sa1111 = platform_device_register_full(&sa1111_devinfo);
356ced8d21cSRussell King 
357d2e539a5SRussell King 	smc91x_resources[0].parent = smc91x_res;
358d2e539a5SRussell King 	smc91x_resources[1].parent = smc91x_res;
359ced8d21cSRussell King 	smc91x_resources[2].start = d->irq_base + NEP_IRQ_SMC91X;
360ced8d21cSRussell King 	smc91x_resources[2].end = d->irq_base + NEP_IRQ_SMC91X;
3619590e898SRussell King 	d->smc91x = platform_device_register_full(&smc91x_devinfo);
3629590e898SRussell King 
363ae14c2e2SRussell King 	platform_set_drvdata(dev, d);
364ae14c2e2SRussell King 
365ae14c2e2SRussell King 	return 0;
366ae14c2e2SRussell King 
367ced8d21cSRussell King  err_irq_alloc:
368f942b0fdSRussell King  err_id:
369f942b0fdSRussell King 	iounmap(d->base);
370f942b0fdSRussell King  err_ioremap:
371ced8d21cSRussell King 	kfree(d);
372ae14c2e2SRussell King  err_alloc:
373ae14c2e2SRussell King 	return ret;
374ae14c2e2SRussell King }
375ae14c2e2SRussell King 
376351a102dSGreg Kroah-Hartman static int neponset_remove(struct platform_device *dev)
377ae14c2e2SRussell King {
378ae14c2e2SRussell King 	struct neponset_drvdata *d = platform_get_drvdata(dev);
379b6bdfcf5SRussell King 	int irq = platform_get_irq(dev, 0);
380ae14c2e2SRussell King 
3819590e898SRussell King 	if (!IS_ERR(d->sa1111))
3829590e898SRussell King 		platform_device_unregister(d->sa1111);
3839590e898SRussell King 	if (!IS_ERR(d->smc91x))
3849590e898SRussell King 		platform_device_unregister(d->smc91x);
385b6bdfcf5SRussell King 	irq_set_chained_handler(irq, NULL);
386ced8d21cSRussell King 	irq_free_descs(d->irq_base, NEP_IRQ_NR);
387f942b0fdSRussell King 	nep_base = NULL;
388f942b0fdSRussell King 	iounmap(d->base);
389ae14c2e2SRussell King 	kfree(d);
390ae14c2e2SRussell King 
3911da177e4SLinus Torvalds 	return 0;
3921da177e4SLinus Torvalds }
3931da177e4SLinus Torvalds 
39451f93390SRussell King #ifdef CONFIG_PM_SLEEP
39551f93390SRussell King static int neponset_suspend(struct device *dev)
3961da177e4SLinus Torvalds {
39751f93390SRussell King 	struct neponset_drvdata *d = dev_get_drvdata(dev);
398ae14c2e2SRussell King 
399f942b0fdSRussell King 	d->ncr0 = readb_relaxed(d->base + NCR_0);
400f942b0fdSRussell King 	d->mdm_ctl_0 = readb_relaxed(d->base + MDM_CTL_0);
4011da177e4SLinus Torvalds 
4021da177e4SLinus Torvalds 	return 0;
4031da177e4SLinus Torvalds }
4041da177e4SLinus Torvalds 
40551f93390SRussell King static int neponset_resume(struct device *dev)
4061da177e4SLinus Torvalds {
40751f93390SRussell King 	struct neponset_drvdata *d = dev_get_drvdata(dev);
408ae14c2e2SRussell King 
409f942b0fdSRussell King 	writeb_relaxed(d->ncr0, d->base + NCR_0);
410f942b0fdSRussell King 	writeb_relaxed(d->mdm_ctl_0, d->base + MDM_CTL_0);
4111da177e4SLinus Torvalds 
4121da177e4SLinus Torvalds 	return 0;
4131da177e4SLinus Torvalds }
4141da177e4SLinus Torvalds 
41551f93390SRussell King static const struct dev_pm_ops neponset_pm_ops = {
41651f93390SRussell King 	.suspend_noirq = neponset_suspend,
41751f93390SRussell King 	.resume_noirq = neponset_resume,
41851f93390SRussell King 	.freeze_noirq = neponset_suspend,
41951f93390SRussell King 	.restore_noirq = neponset_resume,
42051f93390SRussell King };
42151f93390SRussell King #define PM_OPS &neponset_pm_ops
4221da177e4SLinus Torvalds #else
42351f93390SRussell King #define PM_OPS NULL
4241da177e4SLinus Torvalds #endif
4251da177e4SLinus Torvalds 
4263ae5eaecSRussell King static struct platform_driver neponset_device_driver = {
4271da177e4SLinus Torvalds 	.probe		= neponset_probe,
428351a102dSGreg Kroah-Hartman 	.remove		= neponset_remove,
4293ae5eaecSRussell King 	.driver		= {
4303ae5eaecSRussell King 		.name	= "neponset",
43151f93390SRussell King 		.pm	= PM_OPS,
4323ae5eaecSRussell King 	},
4331da177e4SLinus Torvalds };
4341da177e4SLinus Torvalds 
4351da177e4SLinus Torvalds static int __init neponset_init(void)
4361da177e4SLinus Torvalds {
437bab50a35SRussell King 	return platform_driver_register(&neponset_device_driver);
4381da177e4SLinus Torvalds }
4391da177e4SLinus Torvalds 
4401da177e4SLinus Torvalds subsys_initcall(neponset_init);
441