xref: /openbmc/linux/arch/arm/mach-sa1100/neponset.c (revision 351a102d)
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>
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds #include <asm/mach-types.h>
171da177e4SLinus Torvalds #include <asm/mach/map.h>
181da177e4SLinus Torvalds #include <asm/hardware/sa1111.h>
191da177e4SLinus Torvalds #include <asm/sizes.h>
201da177e4SLinus Torvalds 
2192e617d9SRussell King #include <mach/hardware.h>
2292e617d9SRussell King #include <mach/assabet.h>
2392e617d9SRussell King #include <mach/neponset.h>
24f314f33bSRob Herring #include <mach/irqs.h>
2592e617d9SRussell King 
26ced8d21cSRussell King #define NEP_IRQ_SMC91X	0
27ced8d21cSRussell King #define NEP_IRQ_USAR	1
28ced8d21cSRussell King #define NEP_IRQ_SA1111	2
29ced8d21cSRussell King #define NEP_IRQ_NR	3
30ced8d21cSRussell King 
31f942b0fdSRussell King #define WHOAMI		0x00
32f942b0fdSRussell King #define LEDS		0x10
33f942b0fdSRussell King #define SWPK		0x20
34f942b0fdSRussell King #define IRR		0x24
35f942b0fdSRussell King #define KP_Y_IN		0x80
36f942b0fdSRussell King #define KP_X_OUT	0x90
37f942b0fdSRussell King #define NCR_0		0xa0
38f942b0fdSRussell King #define MDM_CTL_0	0xb0
39f942b0fdSRussell King #define MDM_CTL_1	0xb4
40f942b0fdSRussell King #define AUD_CTL		0xc0
41f942b0fdSRussell King 
42f942b0fdSRussell King #define IRR_ETHERNET	(1 << 0)
43f942b0fdSRussell King #define IRR_USAR	(1 << 1)
44f942b0fdSRussell King #define IRR_SA1111	(1 << 2)
45f942b0fdSRussell King 
46f942b0fdSRussell King #define MDM_CTL0_RTS1	(1 << 0)
47f942b0fdSRussell King #define MDM_CTL0_DTR1	(1 << 1)
48f942b0fdSRussell King #define MDM_CTL0_RTS2	(1 << 2)
49f942b0fdSRussell King #define MDM_CTL0_DTR2	(1 << 3)
50f942b0fdSRussell King 
51f942b0fdSRussell King #define MDM_CTL1_CTS1	(1 << 0)
52f942b0fdSRussell King #define MDM_CTL1_DSR1	(1 << 1)
53f942b0fdSRussell King #define MDM_CTL1_DCD1	(1 << 2)
54f942b0fdSRussell King #define MDM_CTL1_CTS2	(1 << 3)
55f942b0fdSRussell King #define MDM_CTL1_DSR2	(1 << 4)
56f942b0fdSRussell King #define MDM_CTL1_DCD2	(1 << 5)
57f942b0fdSRussell King 
58f942b0fdSRussell King #define AUD_SEL_1341	(1 << 0)
59f942b0fdSRussell King #define AUD_MUTE_1341	(1 << 1)
60f942b0fdSRussell King 
61bab50a35SRussell King extern void sa1110_mb_disable(void);
62bab50a35SRussell King 
63ae14c2e2SRussell King struct neponset_drvdata {
64f942b0fdSRussell King 	void __iomem *base;
659590e898SRussell King 	struct platform_device *sa1111;
669590e898SRussell King 	struct platform_device *smc91x;
67ced8d21cSRussell King 	unsigned irq_base;
68ae14c2e2SRussell King #ifdef CONFIG_PM_SLEEP
69ae14c2e2SRussell King 	u32 ncr0;
70ae14c2e2SRussell King 	u32 mdm_ctl_0;
71ae14c2e2SRussell King #endif
72ae14c2e2SRussell King };
73ae14c2e2SRussell King 
74f942b0fdSRussell King static void __iomem *nep_base;
75f942b0fdSRussell King 
766ad1b614SRussell King void neponset_ncr_frob(unsigned int mask, unsigned int val)
776ad1b614SRussell King {
78f942b0fdSRussell King 	void __iomem *base = nep_base;
79f942b0fdSRussell King 
80f942b0fdSRussell King 	if (base) {
816ad1b614SRussell King 		unsigned long flags;
82f942b0fdSRussell King 		unsigned v;
836ad1b614SRussell King 
846ad1b614SRussell King 		local_irq_save(flags);
85f942b0fdSRussell King 		v = readb_relaxed(base + NCR_0);
86f942b0fdSRussell King 		writeb_relaxed((v & ~mask) | val, base + NCR_0);
876ad1b614SRussell King 		local_irq_restore(flags);
88f942b0fdSRussell King 	} else {
89f942b0fdSRussell King 		WARN(1, "nep_base unset\n");
90f942b0fdSRussell King 	}
916ad1b614SRussell King }
92ef0c1484SRussell King EXPORT_SYMBOL(neponset_ncr_frob);
936ad1b614SRussell King 
941da177e4SLinus Torvalds static void neponset_set_mctrl(struct uart_port *port, u_int mctrl)
951da177e4SLinus Torvalds {
96f942b0fdSRussell King 	void __iomem *base = nep_base;
97f942b0fdSRussell King 	u_int mdm_ctl0;
981da177e4SLinus Torvalds 
99f942b0fdSRussell King 	if (!base)
100f942b0fdSRussell King 		return;
101f942b0fdSRussell King 
102f942b0fdSRussell King 	mdm_ctl0 = readb_relaxed(base + MDM_CTL_0);
1031da177e4SLinus Torvalds 	if (port->mapbase == _Ser1UTCR0) {
1041da177e4SLinus Torvalds 		if (mctrl & TIOCM_RTS)
1051da177e4SLinus Torvalds 			mdm_ctl0 &= ~MDM_CTL0_RTS2;
1061da177e4SLinus Torvalds 		else
1071da177e4SLinus Torvalds 			mdm_ctl0 |= MDM_CTL0_RTS2;
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds 		if (mctrl & TIOCM_DTR)
1101da177e4SLinus Torvalds 			mdm_ctl0 &= ~MDM_CTL0_DTR2;
1111da177e4SLinus Torvalds 		else
1121da177e4SLinus Torvalds 			mdm_ctl0 |= MDM_CTL0_DTR2;
1131da177e4SLinus Torvalds 	} else if (port->mapbase == _Ser3UTCR0) {
1141da177e4SLinus Torvalds 		if (mctrl & TIOCM_RTS)
1151da177e4SLinus Torvalds 			mdm_ctl0 &= ~MDM_CTL0_RTS1;
1161da177e4SLinus Torvalds 		else
1171da177e4SLinus Torvalds 			mdm_ctl0 |= MDM_CTL0_RTS1;
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds 		if (mctrl & TIOCM_DTR)
1201da177e4SLinus Torvalds 			mdm_ctl0 &= ~MDM_CTL0_DTR1;
1211da177e4SLinus Torvalds 		else
1221da177e4SLinus Torvalds 			mdm_ctl0 |= MDM_CTL0_DTR1;
1231da177e4SLinus Torvalds 	}
1241da177e4SLinus Torvalds 
125f942b0fdSRussell King 	writeb_relaxed(mdm_ctl0, base + MDM_CTL_0);
1261da177e4SLinus Torvalds }
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds static u_int neponset_get_mctrl(struct uart_port *port)
1291da177e4SLinus Torvalds {
130f942b0fdSRussell King 	void __iomem *base = nep_base;
1311da177e4SLinus Torvalds 	u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR;
132f942b0fdSRussell King 	u_int mdm_ctl1;
1331da177e4SLinus Torvalds 
134f942b0fdSRussell King 	if (!base)
135f942b0fdSRussell King 		return ret;
136f942b0fdSRussell King 
137f942b0fdSRussell King 	mdm_ctl1 = readb_relaxed(base + MDM_CTL_1);
1381da177e4SLinus Torvalds 	if (port->mapbase == _Ser1UTCR0) {
1391da177e4SLinus Torvalds 		if (mdm_ctl1 & MDM_CTL1_DCD2)
1401da177e4SLinus Torvalds 			ret &= ~TIOCM_CD;
1411da177e4SLinus Torvalds 		if (mdm_ctl1 & MDM_CTL1_CTS2)
1421da177e4SLinus Torvalds 			ret &= ~TIOCM_CTS;
1431da177e4SLinus Torvalds 		if (mdm_ctl1 & MDM_CTL1_DSR2)
1441da177e4SLinus Torvalds 			ret &= ~TIOCM_DSR;
1451da177e4SLinus Torvalds 	} else if (port->mapbase == _Ser3UTCR0) {
1461da177e4SLinus Torvalds 		if (mdm_ctl1 & MDM_CTL1_DCD1)
1471da177e4SLinus Torvalds 			ret &= ~TIOCM_CD;
1481da177e4SLinus Torvalds 		if (mdm_ctl1 & MDM_CTL1_CTS1)
1491da177e4SLinus Torvalds 			ret &= ~TIOCM_CTS;
1501da177e4SLinus Torvalds 		if (mdm_ctl1 & MDM_CTL1_DSR1)
1511da177e4SLinus Torvalds 			ret &= ~TIOCM_DSR;
1521da177e4SLinus Torvalds 	}
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds 	return ret;
1551da177e4SLinus Torvalds }
1561da177e4SLinus Torvalds 
157351a102dSGreg Kroah-Hartman static struct sa1100_port_fns neponset_port_fns = {
1581da177e4SLinus Torvalds 	.set_mctrl	= neponset_set_mctrl,
1591da177e4SLinus Torvalds 	.get_mctrl	= neponset_get_mctrl,
1601da177e4SLinus Torvalds };
1611da177e4SLinus Torvalds 
16271045520SRussell King /*
16392e617d9SRussell King  * Install handler for Neponset IRQ.  Note that we have to loop here
16492e617d9SRussell King  * since the ETHERNET and USAR IRQs are level based, and we need to
16592e617d9SRussell King  * ensure that the IRQ signal is deasserted before returning.  This
16692e617d9SRussell King  * is rather unfortunate.
16792e617d9SRussell King  */
168ced8d21cSRussell King static void neponset_irq_handler(unsigned int irq, struct irq_desc *desc)
16992e617d9SRussell King {
170ced8d21cSRussell King 	struct neponset_drvdata *d = irq_desc_get_handler_data(desc);
17192e617d9SRussell King 	unsigned int irr;
17292e617d9SRussell King 
17392e617d9SRussell King 	while (1) {
17492e617d9SRussell King 		/*
17592e617d9SRussell King 		 * Acknowledge the parent IRQ.
17692e617d9SRussell King 		 */
17792e617d9SRussell King 		desc->irq_data.chip->irq_ack(&desc->irq_data);
17892e617d9SRussell King 
17992e617d9SRussell King 		/*
18092e617d9SRussell King 		 * Read the interrupt reason register.  Let's have all
18192e617d9SRussell King 		 * active IRQ bits high.  Note: there is a typo in the
18292e617d9SRussell King 		 * Neponset user's guide for the SA1111 IRR level.
18392e617d9SRussell King 		 */
184f942b0fdSRussell King 		irr = readb_relaxed(d->base + IRR);
185f942b0fdSRussell King 		irr ^= IRR_ETHERNET | IRR_USAR;
18692e617d9SRussell King 
18792e617d9SRussell King 		if ((irr & (IRR_ETHERNET | IRR_USAR | IRR_SA1111)) == 0)
18892e617d9SRussell King 			break;
18992e617d9SRussell King 
19092e617d9SRussell King 		/*
19192e617d9SRussell King 		 * Since there is no individual mask, we have to
19292e617d9SRussell King 		 * mask the parent IRQ.  This is safe, since we'll
19392e617d9SRussell King 		 * recheck the register for any pending IRQs.
19492e617d9SRussell King 		 */
19592e617d9SRussell King 		if (irr & (IRR_ETHERNET | IRR_USAR)) {
19692e617d9SRussell King 			desc->irq_data.chip->irq_mask(&desc->irq_data);
19792e617d9SRussell King 
19892e617d9SRussell King 			/*
19992e617d9SRussell King 			 * Ack the interrupt now to prevent re-entering
20092e617d9SRussell King 			 * this neponset handler.  Again, this is safe
20192e617d9SRussell King 			 * since we'll check the IRR register prior to
20292e617d9SRussell King 			 * leaving.
20392e617d9SRussell King 			 */
20492e617d9SRussell King 			desc->irq_data.chip->irq_ack(&desc->irq_data);
20592e617d9SRussell King 
206ced8d21cSRussell King 			if (irr & IRR_ETHERNET)
207ced8d21cSRussell King 				generic_handle_irq(d->irq_base + NEP_IRQ_SMC91X);
20892e617d9SRussell King 
209ced8d21cSRussell King 			if (irr & IRR_USAR)
210ced8d21cSRussell King 				generic_handle_irq(d->irq_base + NEP_IRQ_USAR);
21192e617d9SRussell King 
21292e617d9SRussell King 			desc->irq_data.chip->irq_unmask(&desc->irq_data);
21392e617d9SRussell King 		}
21492e617d9SRussell King 
215ced8d21cSRussell King 		if (irr & IRR_SA1111)
216ced8d21cSRussell King 			generic_handle_irq(d->irq_base + NEP_IRQ_SA1111);
21792e617d9SRussell King 	}
21892e617d9SRussell King }
21992e617d9SRussell King 
220ced8d21cSRussell King /* Yes, we really do not have any kind of masking or unmasking */
22171045520SRussell King static void nochip_noop(struct irq_data *irq)
22271045520SRussell King {
22371045520SRussell King }
22471045520SRussell King 
22571045520SRussell King static struct irq_chip nochip = {
22671045520SRussell King 	.name = "neponset",
22771045520SRussell King 	.irq_ack = nochip_noop,
22871045520SRussell King 	.irq_mask = nochip_noop,
22971045520SRussell King 	.irq_unmask = nochip_noop,
23071045520SRussell King };
23171045520SRussell King 
23292e617d9SRussell King static struct sa1111_platform_data sa1111_info = {
23307be45f5SRussell King 	.disable_devs	= SA1111_DEVID_PS2_MSE,
23492e617d9SRussell King };
23592e617d9SRussell King 
236351a102dSGreg Kroah-Hartman static int neponset_probe(struct platform_device *dev)
2371da177e4SLinus Torvalds {
238ae14c2e2SRussell King 	struct neponset_drvdata *d;
239f942b0fdSRussell King 	struct resource *nep_res, *sa1111_res, *smc91x_res;
240ced8d21cSRussell King 	struct resource sa1111_resources[] = {
241ced8d21cSRussell King 		DEFINE_RES_MEM(0x40000000, SZ_8K),
242ced8d21cSRussell King 		{ .flags = IORESOURCE_IRQ },
243ced8d21cSRussell King 	};
2449590e898SRussell King 	struct platform_device_info sa1111_devinfo = {
2459590e898SRussell King 		.parent = &dev->dev,
2469590e898SRussell King 		.name = "sa1111",
2479590e898SRussell King 		.id = 0,
2489590e898SRussell King 		.res = sa1111_resources,
2499590e898SRussell King 		.num_res = ARRAY_SIZE(sa1111_resources),
2509590e898SRussell King 		.data = &sa1111_info,
2519590e898SRussell King 		.size_data = sizeof(sa1111_info),
2529590e898SRussell King 		.dma_mask = 0xffffffffUL,
2539590e898SRussell King 	};
254ced8d21cSRussell King 	struct resource smc91x_resources[] = {
255ced8d21cSRussell King 		DEFINE_RES_MEM_NAMED(SA1100_CS3_PHYS,
256ced8d21cSRussell King 			0x02000000, "smc91x-regs"),
257ced8d21cSRussell King 		DEFINE_RES_MEM_NAMED(SA1100_CS3_PHYS + 0x02000000,
258ced8d21cSRussell King 			0x02000000, "smc91x-attrib"),
259ced8d21cSRussell King 		{ .flags = IORESOURCE_IRQ },
260ced8d21cSRussell King 	};
2619590e898SRussell King 	struct platform_device_info smc91x_devinfo = {
2629590e898SRussell King 		.parent = &dev->dev,
2639590e898SRussell King 		.name = "smc91x",
2649590e898SRussell King 		.id = 0,
2659590e898SRussell King 		.res = smc91x_resources,
2669590e898SRussell King 		.num_res = ARRAY_SIZE(smc91x_resources),
2679590e898SRussell King 	};
268b6bdfcf5SRussell King 	int ret, irq;
269b6bdfcf5SRussell King 
270f942b0fdSRussell King 	if (nep_base)
271f942b0fdSRussell King 		return -EBUSY;
272f942b0fdSRussell King 
273b6bdfcf5SRussell King 	irq = ret = platform_get_irq(dev, 0);
274b6bdfcf5SRussell King 	if (ret < 0)
275b6bdfcf5SRussell King 		goto err_alloc;
276ae14c2e2SRussell King 
277f942b0fdSRussell King 	nep_res = platform_get_resource(dev, IORESOURCE_MEM, 0);
278d2e539a5SRussell King 	smc91x_res = platform_get_resource(dev, IORESOURCE_MEM, 1);
279d2e539a5SRussell King 	sa1111_res = platform_get_resource(dev, IORESOURCE_MEM, 2);
280f942b0fdSRussell King 	if (!nep_res || !smc91x_res || !sa1111_res) {
281d2e539a5SRussell King 		ret = -ENXIO;
282d2e539a5SRussell King 		goto err_alloc;
283d2e539a5SRussell King 	}
284d2e539a5SRussell King 
285ae14c2e2SRussell King 	d = kzalloc(sizeof(*d), GFP_KERNEL);
286ae14c2e2SRussell King 	if (!d) {
287ae14c2e2SRussell King 		ret = -ENOMEM;
288ae14c2e2SRussell King 		goto err_alloc;
289ae14c2e2SRussell King 	}
290ae14c2e2SRussell King 
291f942b0fdSRussell King 	d->base = ioremap(nep_res->start, SZ_4K);
292f942b0fdSRussell King 	if (!d->base) {
293f942b0fdSRussell King 		ret = -ENOMEM;
294f942b0fdSRussell King 		goto err_ioremap;
295f942b0fdSRussell King 	}
296f942b0fdSRussell King 
297f942b0fdSRussell King 	if (readb_relaxed(d->base + WHOAMI) != 0x11) {
298f942b0fdSRussell King 		dev_warn(&dev->dev, "Neponset board detected, but wrong ID: %02x\n",
299f942b0fdSRussell King 			 readb_relaxed(d->base + WHOAMI));
300f942b0fdSRussell King 		ret = -ENODEV;
301f942b0fdSRussell King 		goto err_id;
302f942b0fdSRussell King 	}
303f942b0fdSRussell King 
304ced8d21cSRussell King 	ret = irq_alloc_descs(-1, IRQ_BOARD_START, NEP_IRQ_NR, -1);
305ced8d21cSRussell King 	if (ret <= 0) {
306ced8d21cSRussell King 		dev_err(&dev->dev, "unable to allocate %u irqs: %d\n",
307ced8d21cSRussell King 			NEP_IRQ_NR, ret);
308ced8d21cSRussell King 		if (ret == 0)
309ced8d21cSRussell King 			ret = -ENOMEM;
310ced8d21cSRussell King 		goto err_irq_alloc;
311ced8d21cSRussell King 	}
3121da177e4SLinus Torvalds 
313ced8d21cSRussell King 	d->irq_base = ret;
314ced8d21cSRussell King 
315ced8d21cSRussell King 	irq_set_chip_and_handler(d->irq_base + NEP_IRQ_SMC91X, &nochip,
316ced8d21cSRussell King 		handle_simple_irq);
317ced8d21cSRussell King 	set_irq_flags(d->irq_base + NEP_IRQ_SMC91X, IRQF_VALID | IRQF_PROBE);
318ced8d21cSRussell King 	irq_set_chip_and_handler(d->irq_base + NEP_IRQ_USAR, &nochip,
319ced8d21cSRussell King 		handle_simple_irq);
320ced8d21cSRussell King 	set_irq_flags(d->irq_base + NEP_IRQ_USAR, IRQF_VALID | IRQF_PROBE);
321ced8d21cSRussell King 	irq_set_chip(d->irq_base + NEP_IRQ_SA1111, &nochip);
322ced8d21cSRussell King 
323b6bdfcf5SRussell King 	irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
324b6bdfcf5SRussell King 	irq_set_handler_data(irq, d);
325b6bdfcf5SRussell King 	irq_set_chained_handler(irq, neponset_irq_handler);
3261da177e4SLinus Torvalds 
3271da177e4SLinus Torvalds 	/*
328ced8d21cSRussell King 	 * We would set IRQ_GPIO25 to be a wake-up IRQ, but unfortunately
329ced8d21cSRussell King 	 * something on the Neponset activates this IRQ on sleep (eth?)
3301da177e4SLinus Torvalds 	 */
3311da177e4SLinus Torvalds #if 0
332b6bdfcf5SRussell King 	enable_irq_wake(irq);
3331da177e4SLinus Torvalds #endif
3341da177e4SLinus Torvalds 
335ced8d21cSRussell King 	dev_info(&dev->dev, "Neponset daughter board, providing IRQ%u-%u\n",
336ced8d21cSRussell King 		 d->irq_base, d->irq_base + NEP_IRQ_NR - 1);
337f942b0fdSRussell King 	nep_base = d->base;
3381da177e4SLinus Torvalds 
339ced8d21cSRussell King 	sa1100_register_uart_fns(&neponset_port_fns);
340ced8d21cSRussell King 
341bab50a35SRussell King 	/* Ensure that the memory bus request/grant signals are setup */
342bab50a35SRussell King 	sa1110_mb_disable();
343bab50a35SRussell King 
344ced8d21cSRussell King 	/* Disable GPIO 0/1 drivers so the buttons work on the Assabet */
345f942b0fdSRussell King 	writeb_relaxed(NCR_GP01_OFF, d->base + NCR_0);
3461da177e4SLinus Torvalds 
347d2e539a5SRussell King 	sa1111_resources[0].parent = sa1111_res;
348ced8d21cSRussell King 	sa1111_resources[1].start = d->irq_base + NEP_IRQ_SA1111;
349ced8d21cSRussell King 	sa1111_resources[1].end = d->irq_base + NEP_IRQ_SA1111;
3509590e898SRussell King 	d->sa1111 = platform_device_register_full(&sa1111_devinfo);
351ced8d21cSRussell King 
352d2e539a5SRussell King 	smc91x_resources[0].parent = smc91x_res;
353d2e539a5SRussell King 	smc91x_resources[1].parent = smc91x_res;
354ced8d21cSRussell King 	smc91x_resources[2].start = d->irq_base + NEP_IRQ_SMC91X;
355ced8d21cSRussell King 	smc91x_resources[2].end = d->irq_base + NEP_IRQ_SMC91X;
3569590e898SRussell King 	d->smc91x = platform_device_register_full(&smc91x_devinfo);
3579590e898SRussell King 
358ae14c2e2SRussell King 	platform_set_drvdata(dev, d);
359ae14c2e2SRussell King 
360ae14c2e2SRussell King 	return 0;
361ae14c2e2SRussell King 
362ced8d21cSRussell King  err_irq_alloc:
363f942b0fdSRussell King  err_id:
364f942b0fdSRussell King 	iounmap(d->base);
365f942b0fdSRussell King  err_ioremap:
366ced8d21cSRussell King 	kfree(d);
367ae14c2e2SRussell King  err_alloc:
368ae14c2e2SRussell King 	return ret;
369ae14c2e2SRussell King }
370ae14c2e2SRussell King 
371351a102dSGreg Kroah-Hartman static int neponset_remove(struct platform_device *dev)
372ae14c2e2SRussell King {
373ae14c2e2SRussell King 	struct neponset_drvdata *d = platform_get_drvdata(dev);
374b6bdfcf5SRussell King 	int irq = platform_get_irq(dev, 0);
375ae14c2e2SRussell King 
3769590e898SRussell King 	if (!IS_ERR(d->sa1111))
3779590e898SRussell King 		platform_device_unregister(d->sa1111);
3789590e898SRussell King 	if (!IS_ERR(d->smc91x))
3799590e898SRussell King 		platform_device_unregister(d->smc91x);
380b6bdfcf5SRussell King 	irq_set_chained_handler(irq, NULL);
381ced8d21cSRussell King 	irq_free_descs(d->irq_base, NEP_IRQ_NR);
382f942b0fdSRussell King 	nep_base = NULL;
383f942b0fdSRussell King 	iounmap(d->base);
384ae14c2e2SRussell King 	kfree(d);
385ae14c2e2SRussell King 
3861da177e4SLinus Torvalds 	return 0;
3871da177e4SLinus Torvalds }
3881da177e4SLinus Torvalds 
38951f93390SRussell King #ifdef CONFIG_PM_SLEEP
39051f93390SRussell King static int neponset_suspend(struct device *dev)
3911da177e4SLinus Torvalds {
39251f93390SRussell King 	struct neponset_drvdata *d = dev_get_drvdata(dev);
393ae14c2e2SRussell King 
394f942b0fdSRussell King 	d->ncr0 = readb_relaxed(d->base + NCR_0);
395f942b0fdSRussell King 	d->mdm_ctl_0 = readb_relaxed(d->base + MDM_CTL_0);
3961da177e4SLinus Torvalds 
3971da177e4SLinus Torvalds 	return 0;
3981da177e4SLinus Torvalds }
3991da177e4SLinus Torvalds 
40051f93390SRussell King static int neponset_resume(struct device *dev)
4011da177e4SLinus Torvalds {
40251f93390SRussell King 	struct neponset_drvdata *d = dev_get_drvdata(dev);
403ae14c2e2SRussell King 
404f942b0fdSRussell King 	writeb_relaxed(d->ncr0, d->base + NCR_0);
405f942b0fdSRussell King 	writeb_relaxed(d->mdm_ctl_0, d->base + MDM_CTL_0);
4061da177e4SLinus Torvalds 
4071da177e4SLinus Torvalds 	return 0;
4081da177e4SLinus Torvalds }
4091da177e4SLinus Torvalds 
41051f93390SRussell King static const struct dev_pm_ops neponset_pm_ops = {
41151f93390SRussell King 	.suspend_noirq = neponset_suspend,
41251f93390SRussell King 	.resume_noirq = neponset_resume,
41351f93390SRussell King 	.freeze_noirq = neponset_suspend,
41451f93390SRussell King 	.restore_noirq = neponset_resume,
41551f93390SRussell King };
41651f93390SRussell King #define PM_OPS &neponset_pm_ops
4171da177e4SLinus Torvalds #else
41851f93390SRussell King #define PM_OPS NULL
4191da177e4SLinus Torvalds #endif
4201da177e4SLinus Torvalds 
4213ae5eaecSRussell King static struct platform_driver neponset_device_driver = {
4221da177e4SLinus Torvalds 	.probe		= neponset_probe,
423351a102dSGreg Kroah-Hartman 	.remove		= neponset_remove,
4243ae5eaecSRussell King 	.driver		= {
4253ae5eaecSRussell King 		.name	= "neponset",
426398e58d0SRussell King 		.owner	= THIS_MODULE,
42751f93390SRussell King 		.pm	= PM_OPS,
4283ae5eaecSRussell King 	},
4291da177e4SLinus Torvalds };
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds static int __init neponset_init(void)
4321da177e4SLinus Torvalds {
433bab50a35SRussell King 	return platform_driver_register(&neponset_device_driver);
4341da177e4SLinus Torvalds }
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds subsys_initcall(neponset_init);
437