xref: /openbmc/linux/arch/arm/mach-sa1100/neponset.c (revision b2441318)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * linux/arch/arm/mach-sa1100/neponset.c
41da177e4SLinus Torvalds  */
59590e898SRussell King #include <linux/err.h>
61da177e4SLinus Torvalds #include <linux/init.h>
71da177e4SLinus Torvalds #include <linux/ioport.h>
8ced8d21cSRussell King #include <linux/irq.h>
992e617d9SRussell King #include <linux/kernel.h>
10ae14c2e2SRussell King #include <linux/module.h>
116920b5a7SRussell King #include <linux/platform_data/sa11x0-serial.h>
12d052d1beSRussell King #include <linux/platform_device.h>
1351f93390SRussell King #include <linux/pm.h>
1492e617d9SRussell King #include <linux/serial_core.h>
15ae14c2e2SRussell King #include <linux/slab.h>
16b70661c7SArnd Bergmann #include <linux/smc91x.h>
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds #include <asm/mach-types.h>
191da177e4SLinus Torvalds #include <asm/mach/map.h>
201da177e4SLinus Torvalds #include <asm/hardware/sa1111.h>
211da177e4SLinus Torvalds #include <asm/sizes.h>
221da177e4SLinus Torvalds 
2392e617d9SRussell King #include <mach/hardware.h>
2492e617d9SRussell King #include <mach/assabet.h>
2592e617d9SRussell King #include <mach/neponset.h>
26f314f33bSRob Herring #include <mach/irqs.h>
2792e617d9SRussell King 
28ced8d21cSRussell King #define NEP_IRQ_SMC91X	0
29ced8d21cSRussell King #define NEP_IRQ_USAR	1
30ced8d21cSRussell King #define NEP_IRQ_SA1111	2
31ced8d21cSRussell King #define NEP_IRQ_NR	3
32ced8d21cSRussell King 
33f942b0fdSRussell King #define WHOAMI		0x00
34f942b0fdSRussell King #define LEDS		0x10
35f942b0fdSRussell King #define SWPK		0x20
36f942b0fdSRussell King #define IRR		0x24
37f942b0fdSRussell King #define KP_Y_IN		0x80
38f942b0fdSRussell King #define KP_X_OUT	0x90
39f942b0fdSRussell King #define NCR_0		0xa0
40f942b0fdSRussell King #define MDM_CTL_0	0xb0
41f942b0fdSRussell King #define MDM_CTL_1	0xb4
42f942b0fdSRussell King #define AUD_CTL		0xc0
43f942b0fdSRussell King 
44f942b0fdSRussell King #define IRR_ETHERNET	(1 << 0)
45f942b0fdSRussell King #define IRR_USAR	(1 << 1)
46f942b0fdSRussell King #define IRR_SA1111	(1 << 2)
47f942b0fdSRussell King 
48f942b0fdSRussell King #define MDM_CTL0_RTS1	(1 << 0)
49f942b0fdSRussell King #define MDM_CTL0_DTR1	(1 << 1)
50f942b0fdSRussell King #define MDM_CTL0_RTS2	(1 << 2)
51f942b0fdSRussell King #define MDM_CTL0_DTR2	(1 << 3)
52f942b0fdSRussell King 
53f942b0fdSRussell King #define MDM_CTL1_CTS1	(1 << 0)
54f942b0fdSRussell King #define MDM_CTL1_DSR1	(1 << 1)
55f942b0fdSRussell King #define MDM_CTL1_DCD1	(1 << 2)
56f942b0fdSRussell King #define MDM_CTL1_CTS2	(1 << 3)
57f942b0fdSRussell King #define MDM_CTL1_DSR2	(1 << 4)
58f942b0fdSRussell King #define MDM_CTL1_DCD2	(1 << 5)
59f942b0fdSRussell King 
60f942b0fdSRussell King #define AUD_SEL_1341	(1 << 0)
61f942b0fdSRussell King #define AUD_MUTE_1341	(1 << 1)
62f942b0fdSRussell King 
63bab50a35SRussell King extern void sa1110_mb_disable(void);
64bab50a35SRussell King 
65ae14c2e2SRussell King struct neponset_drvdata {
66f942b0fdSRussell King 	void __iomem *base;
679590e898SRussell King 	struct platform_device *sa1111;
689590e898SRussell King 	struct platform_device *smc91x;
69ced8d21cSRussell King 	unsigned irq_base;
70ae14c2e2SRussell King #ifdef CONFIG_PM_SLEEP
71ae14c2e2SRussell King 	u32 ncr0;
72ae14c2e2SRussell King 	u32 mdm_ctl_0;
73ae14c2e2SRussell King #endif
74ae14c2e2SRussell King };
75ae14c2e2SRussell King 
76f942b0fdSRussell King static void __iomem *nep_base;
77f942b0fdSRussell King 
786ad1b614SRussell King void neponset_ncr_frob(unsigned int mask, unsigned int val)
796ad1b614SRussell King {
80f942b0fdSRussell King 	void __iomem *base = nep_base;
81f942b0fdSRussell King 
82f942b0fdSRussell King 	if (base) {
836ad1b614SRussell King 		unsigned long flags;
84f942b0fdSRussell King 		unsigned v;
856ad1b614SRussell King 
866ad1b614SRussell King 		local_irq_save(flags);
87f942b0fdSRussell King 		v = readb_relaxed(base + NCR_0);
88f942b0fdSRussell King 		writeb_relaxed((v & ~mask) | val, base + NCR_0);
896ad1b614SRussell King 		local_irq_restore(flags);
90f942b0fdSRussell King 	} else {
91f942b0fdSRussell King 		WARN(1, "nep_base unset\n");
92f942b0fdSRussell King 	}
936ad1b614SRussell King }
94ef0c1484SRussell King EXPORT_SYMBOL(neponset_ncr_frob);
956ad1b614SRussell King 
961da177e4SLinus Torvalds static void neponset_set_mctrl(struct uart_port *port, u_int mctrl)
971da177e4SLinus Torvalds {
98f942b0fdSRussell King 	void __iomem *base = nep_base;
99f942b0fdSRussell King 	u_int mdm_ctl0;
1001da177e4SLinus Torvalds 
101f942b0fdSRussell King 	if (!base)
102f942b0fdSRussell King 		return;
103f942b0fdSRussell King 
104f942b0fdSRussell King 	mdm_ctl0 = readb_relaxed(base + MDM_CTL_0);
1051da177e4SLinus Torvalds 	if (port->mapbase == _Ser1UTCR0) {
1061da177e4SLinus Torvalds 		if (mctrl & TIOCM_RTS)
1071da177e4SLinus Torvalds 			mdm_ctl0 &= ~MDM_CTL0_RTS2;
1081da177e4SLinus Torvalds 		else
1091da177e4SLinus Torvalds 			mdm_ctl0 |= MDM_CTL0_RTS2;
1101da177e4SLinus Torvalds 
1111da177e4SLinus Torvalds 		if (mctrl & TIOCM_DTR)
1121da177e4SLinus Torvalds 			mdm_ctl0 &= ~MDM_CTL0_DTR2;
1131da177e4SLinus Torvalds 		else
1141da177e4SLinus Torvalds 			mdm_ctl0 |= MDM_CTL0_DTR2;
1151da177e4SLinus Torvalds 	} else if (port->mapbase == _Ser3UTCR0) {
1161da177e4SLinus Torvalds 		if (mctrl & TIOCM_RTS)
1171da177e4SLinus Torvalds 			mdm_ctl0 &= ~MDM_CTL0_RTS1;
1181da177e4SLinus Torvalds 		else
1191da177e4SLinus Torvalds 			mdm_ctl0 |= MDM_CTL0_RTS1;
1201da177e4SLinus Torvalds 
1211da177e4SLinus Torvalds 		if (mctrl & TIOCM_DTR)
1221da177e4SLinus Torvalds 			mdm_ctl0 &= ~MDM_CTL0_DTR1;
1231da177e4SLinus Torvalds 		else
1241da177e4SLinus Torvalds 			mdm_ctl0 |= MDM_CTL0_DTR1;
1251da177e4SLinus Torvalds 	}
1261da177e4SLinus Torvalds 
127f942b0fdSRussell King 	writeb_relaxed(mdm_ctl0, base + MDM_CTL_0);
1281da177e4SLinus Torvalds }
1291da177e4SLinus Torvalds 
1301da177e4SLinus Torvalds static u_int neponset_get_mctrl(struct uart_port *port)
1311da177e4SLinus Torvalds {
132f942b0fdSRussell King 	void __iomem *base = nep_base;
1331da177e4SLinus Torvalds 	u_int ret = TIOCM_CD | TIOCM_CTS | TIOCM_DSR;
134f942b0fdSRussell King 	u_int mdm_ctl1;
1351da177e4SLinus Torvalds 
136f942b0fdSRussell King 	if (!base)
137f942b0fdSRussell King 		return ret;
138f942b0fdSRussell King 
139f942b0fdSRussell King 	mdm_ctl1 = readb_relaxed(base + MDM_CTL_1);
1401da177e4SLinus Torvalds 	if (port->mapbase == _Ser1UTCR0) {
1411da177e4SLinus Torvalds 		if (mdm_ctl1 & MDM_CTL1_DCD2)
1421da177e4SLinus Torvalds 			ret &= ~TIOCM_CD;
1431da177e4SLinus Torvalds 		if (mdm_ctl1 & MDM_CTL1_CTS2)
1441da177e4SLinus Torvalds 			ret &= ~TIOCM_CTS;
1451da177e4SLinus Torvalds 		if (mdm_ctl1 & MDM_CTL1_DSR2)
1461da177e4SLinus Torvalds 			ret &= ~TIOCM_DSR;
1471da177e4SLinus Torvalds 	} else if (port->mapbase == _Ser3UTCR0) {
1481da177e4SLinus Torvalds 		if (mdm_ctl1 & MDM_CTL1_DCD1)
1491da177e4SLinus Torvalds 			ret &= ~TIOCM_CD;
1501da177e4SLinus Torvalds 		if (mdm_ctl1 & MDM_CTL1_CTS1)
1511da177e4SLinus Torvalds 			ret &= ~TIOCM_CTS;
1521da177e4SLinus Torvalds 		if (mdm_ctl1 & MDM_CTL1_DSR1)
1531da177e4SLinus Torvalds 			ret &= ~TIOCM_DSR;
1541da177e4SLinus Torvalds 	}
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds 	return ret;
1571da177e4SLinus Torvalds }
1581da177e4SLinus Torvalds 
159351a102dSGreg Kroah-Hartman static struct sa1100_port_fns neponset_port_fns = {
1601da177e4SLinus Torvalds 	.set_mctrl	= neponset_set_mctrl,
1611da177e4SLinus Torvalds 	.get_mctrl	= neponset_get_mctrl,
1621da177e4SLinus Torvalds };
1631da177e4SLinus Torvalds 
16471045520SRussell King /*
16592e617d9SRussell King  * Install handler for Neponset IRQ.  Note that we have to loop here
16692e617d9SRussell King  * since the ETHERNET and USAR IRQs are level based, and we need to
16792e617d9SRussell King  * ensure that the IRQ signal is deasserted before returning.  This
16892e617d9SRussell King  * is rather unfortunate.
16992e617d9SRussell King  */
170bd0b9ac4SThomas Gleixner static void neponset_irq_handler(struct irq_desc *desc)
17192e617d9SRussell King {
172ced8d21cSRussell King 	struct neponset_drvdata *d = irq_desc_get_handler_data(desc);
17392e617d9SRussell King 	unsigned int irr;
17492e617d9SRussell King 
17592e617d9SRussell King 	while (1) {
17692e617d9SRussell King 		/*
17792e617d9SRussell King 		 * Acknowledge the parent IRQ.
17892e617d9SRussell King 		 */
17992e617d9SRussell King 		desc->irq_data.chip->irq_ack(&desc->irq_data);
18092e617d9SRussell King 
18192e617d9SRussell King 		/*
18292e617d9SRussell King 		 * Read the interrupt reason register.  Let's have all
18392e617d9SRussell King 		 * active IRQ bits high.  Note: there is a typo in the
18492e617d9SRussell King 		 * Neponset user's guide for the SA1111 IRR level.
18592e617d9SRussell King 		 */
186f942b0fdSRussell King 		irr = readb_relaxed(d->base + IRR);
187f942b0fdSRussell King 		irr ^= IRR_ETHERNET | IRR_USAR;
18892e617d9SRussell King 
18992e617d9SRussell King 		if ((irr & (IRR_ETHERNET | IRR_USAR | IRR_SA1111)) == 0)
19092e617d9SRussell King 			break;
19192e617d9SRussell King 
19292e617d9SRussell King 		/*
19392e617d9SRussell King 		 * Since there is no individual mask, we have to
19492e617d9SRussell King 		 * mask the parent IRQ.  This is safe, since we'll
19592e617d9SRussell King 		 * recheck the register for any pending IRQs.
19692e617d9SRussell King 		 */
19792e617d9SRussell King 		if (irr & (IRR_ETHERNET | IRR_USAR)) {
19892e617d9SRussell King 			desc->irq_data.chip->irq_mask(&desc->irq_data);
19992e617d9SRussell King 
20092e617d9SRussell King 			/*
20192e617d9SRussell King 			 * Ack the interrupt now to prevent re-entering
20292e617d9SRussell King 			 * this neponset handler.  Again, this is safe
20392e617d9SRussell King 			 * since we'll check the IRR register prior to
20492e617d9SRussell King 			 * leaving.
20592e617d9SRussell King 			 */
20692e617d9SRussell King 			desc->irq_data.chip->irq_ack(&desc->irq_data);
20792e617d9SRussell King 
208ced8d21cSRussell King 			if (irr & IRR_ETHERNET)
209ced8d21cSRussell King 				generic_handle_irq(d->irq_base + NEP_IRQ_SMC91X);
21092e617d9SRussell King 
211ced8d21cSRussell King 			if (irr & IRR_USAR)
212ced8d21cSRussell King 				generic_handle_irq(d->irq_base + NEP_IRQ_USAR);
21392e617d9SRussell King 
21492e617d9SRussell King 			desc->irq_data.chip->irq_unmask(&desc->irq_data);
21592e617d9SRussell King 		}
21692e617d9SRussell King 
217ced8d21cSRussell King 		if (irr & IRR_SA1111)
218ced8d21cSRussell King 			generic_handle_irq(d->irq_base + NEP_IRQ_SA1111);
21992e617d9SRussell King 	}
22092e617d9SRussell King }
22192e617d9SRussell King 
222ced8d21cSRussell King /* Yes, we really do not have any kind of masking or unmasking */
22371045520SRussell King static void nochip_noop(struct irq_data *irq)
22471045520SRussell King {
22571045520SRussell King }
22671045520SRussell King 
22771045520SRussell King static struct irq_chip nochip = {
22871045520SRussell King 	.name = "neponset",
22971045520SRussell King 	.irq_ack = nochip_noop,
23071045520SRussell King 	.irq_mask = nochip_noop,
23171045520SRussell King 	.irq_unmask = nochip_noop,
23271045520SRussell King };
23371045520SRussell King 
23492e617d9SRussell King static struct sa1111_platform_data sa1111_info = {
23507be45f5SRussell King 	.disable_devs	= SA1111_DEVID_PS2_MSE,
23692e617d9SRussell King };
23792e617d9SRussell King 
238351a102dSGreg Kroah-Hartman static int neponset_probe(struct platform_device *dev)
2391da177e4SLinus Torvalds {
240ae14c2e2SRussell King 	struct neponset_drvdata *d;
241f942b0fdSRussell King 	struct resource *nep_res, *sa1111_res, *smc91x_res;
242ced8d21cSRussell King 	struct resource sa1111_resources[] = {
243ced8d21cSRussell King 		DEFINE_RES_MEM(0x40000000, SZ_8K),
244ced8d21cSRussell King 		{ .flags = IORESOURCE_IRQ },
245ced8d21cSRussell King 	};
2469590e898SRussell King 	struct platform_device_info sa1111_devinfo = {
2479590e898SRussell King 		.parent = &dev->dev,
2489590e898SRussell King 		.name = "sa1111",
2499590e898SRussell King 		.id = 0,
2509590e898SRussell King 		.res = sa1111_resources,
2519590e898SRussell King 		.num_res = ARRAY_SIZE(sa1111_resources),
2529590e898SRussell King 		.data = &sa1111_info,
2539590e898SRussell King 		.size_data = sizeof(sa1111_info),
2549590e898SRussell King 		.dma_mask = 0xffffffffUL,
2559590e898SRussell King 	};
256ced8d21cSRussell King 	struct resource smc91x_resources[] = {
257ced8d21cSRussell King 		DEFINE_RES_MEM_NAMED(SA1100_CS3_PHYS,
258ced8d21cSRussell King 			0x02000000, "smc91x-regs"),
259ced8d21cSRussell King 		DEFINE_RES_MEM_NAMED(SA1100_CS3_PHYS + 0x02000000,
260ced8d21cSRussell King 			0x02000000, "smc91x-attrib"),
261ced8d21cSRussell King 		{ .flags = IORESOURCE_IRQ },
262ced8d21cSRussell King 	};
263b70661c7SArnd Bergmann 	struct smc91x_platdata smc91x_platdata = {
264b70661c7SArnd Bergmann 		.flags = SMC91X_USE_8BIT | SMC91X_IO_SHIFT_2 | SMC91X_NOWAIT,
265b70661c7SArnd Bergmann 	};
2669590e898SRussell King 	struct platform_device_info smc91x_devinfo = {
2679590e898SRussell King 		.parent = &dev->dev,
2689590e898SRussell King 		.name = "smc91x",
2699590e898SRussell King 		.id = 0,
2709590e898SRussell King 		.res = smc91x_resources,
2719590e898SRussell King 		.num_res = ARRAY_SIZE(smc91x_resources),
27204b91701SArnd Bergmann 		.data = &smc91x_platdata,
27304b91701SArnd Bergmann 		.size_data = sizeof(smc91x_platdata),
2749590e898SRussell King 	};
275b6bdfcf5SRussell King 	int ret, irq;
276b6bdfcf5SRussell King 
277f942b0fdSRussell King 	if (nep_base)
278f942b0fdSRussell King 		return -EBUSY;
279f942b0fdSRussell King 
280b6bdfcf5SRussell King 	irq = ret = platform_get_irq(dev, 0);
281b6bdfcf5SRussell King 	if (ret < 0)
282b6bdfcf5SRussell King 		goto err_alloc;
283ae14c2e2SRussell King 
284f942b0fdSRussell King 	nep_res = platform_get_resource(dev, IORESOURCE_MEM, 0);
285d2e539a5SRussell King 	smc91x_res = platform_get_resource(dev, IORESOURCE_MEM, 1);
286d2e539a5SRussell King 	sa1111_res = platform_get_resource(dev, IORESOURCE_MEM, 2);
287f942b0fdSRussell King 	if (!nep_res || !smc91x_res || !sa1111_res) {
288d2e539a5SRussell King 		ret = -ENXIO;
289d2e539a5SRussell King 		goto err_alloc;
290d2e539a5SRussell King 	}
291d2e539a5SRussell King 
292ae14c2e2SRussell King 	d = kzalloc(sizeof(*d), GFP_KERNEL);
293ae14c2e2SRussell King 	if (!d) {
294ae14c2e2SRussell King 		ret = -ENOMEM;
295ae14c2e2SRussell King 		goto err_alloc;
296ae14c2e2SRussell King 	}
297ae14c2e2SRussell King 
298f942b0fdSRussell King 	d->base = ioremap(nep_res->start, SZ_4K);
299f942b0fdSRussell King 	if (!d->base) {
300f942b0fdSRussell King 		ret = -ENOMEM;
301f942b0fdSRussell King 		goto err_ioremap;
302f942b0fdSRussell King 	}
303f942b0fdSRussell King 
304f942b0fdSRussell King 	if (readb_relaxed(d->base + WHOAMI) != 0x11) {
305f942b0fdSRussell King 		dev_warn(&dev->dev, "Neponset board detected, but wrong ID: %02x\n",
306f942b0fdSRussell King 			 readb_relaxed(d->base + WHOAMI));
307f942b0fdSRussell King 		ret = -ENODEV;
308f942b0fdSRussell King 		goto err_id;
309f942b0fdSRussell King 	}
310f942b0fdSRussell King 
311ced8d21cSRussell King 	ret = irq_alloc_descs(-1, IRQ_BOARD_START, NEP_IRQ_NR, -1);
312ced8d21cSRussell King 	if (ret <= 0) {
313ced8d21cSRussell King 		dev_err(&dev->dev, "unable to allocate %u irqs: %d\n",
314ced8d21cSRussell King 			NEP_IRQ_NR, ret);
315ced8d21cSRussell King 		if (ret == 0)
316ced8d21cSRussell King 			ret = -ENOMEM;
317ced8d21cSRussell King 		goto err_irq_alloc;
318ced8d21cSRussell King 	}
3191da177e4SLinus Torvalds 
320ced8d21cSRussell King 	d->irq_base = ret;
321ced8d21cSRussell King 
322ced8d21cSRussell King 	irq_set_chip_and_handler(d->irq_base + NEP_IRQ_SMC91X, &nochip,
323ced8d21cSRussell King 		handle_simple_irq);
324e8d36d5dSRob Herring 	irq_clear_status_flags(d->irq_base + NEP_IRQ_SMC91X, IRQ_NOREQUEST | IRQ_NOPROBE);
325ced8d21cSRussell King 	irq_set_chip_and_handler(d->irq_base + NEP_IRQ_USAR, &nochip,
326ced8d21cSRussell King 		handle_simple_irq);
327e8d36d5dSRob Herring 	irq_clear_status_flags(d->irq_base + NEP_IRQ_USAR, IRQ_NOREQUEST | IRQ_NOPROBE);
328ced8d21cSRussell King 	irq_set_chip(d->irq_base + NEP_IRQ_SA1111, &nochip);
329ced8d21cSRussell King 
330b6bdfcf5SRussell King 	irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
331056c0acfSRussell King 	irq_set_chained_handler_and_data(irq, neponset_irq_handler, d);
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds 	/*
334ced8d21cSRussell King 	 * We would set IRQ_GPIO25 to be a wake-up IRQ, but unfortunately
335ced8d21cSRussell King 	 * something on the Neponset activates this IRQ on sleep (eth?)
3361da177e4SLinus Torvalds 	 */
3371da177e4SLinus Torvalds #if 0
338b6bdfcf5SRussell King 	enable_irq_wake(irq);
3391da177e4SLinus Torvalds #endif
3401da177e4SLinus Torvalds 
341ced8d21cSRussell King 	dev_info(&dev->dev, "Neponset daughter board, providing IRQ%u-%u\n",
342ced8d21cSRussell King 		 d->irq_base, d->irq_base + NEP_IRQ_NR - 1);
343f942b0fdSRussell King 	nep_base = d->base;
3441da177e4SLinus Torvalds 
345ced8d21cSRussell King 	sa1100_register_uart_fns(&neponset_port_fns);
346ced8d21cSRussell King 
347bab50a35SRussell King 	/* Ensure that the memory bus request/grant signals are setup */
348bab50a35SRussell King 	sa1110_mb_disable();
349bab50a35SRussell King 
350ced8d21cSRussell King 	/* Disable GPIO 0/1 drivers so the buttons work on the Assabet */
351f942b0fdSRussell King 	writeb_relaxed(NCR_GP01_OFF, d->base + NCR_0);
3521da177e4SLinus Torvalds 
353d2e539a5SRussell King 	sa1111_resources[0].parent = sa1111_res;
354ced8d21cSRussell King 	sa1111_resources[1].start = d->irq_base + NEP_IRQ_SA1111;
355ced8d21cSRussell King 	sa1111_resources[1].end = d->irq_base + NEP_IRQ_SA1111;
3569590e898SRussell King 	d->sa1111 = platform_device_register_full(&sa1111_devinfo);
357ced8d21cSRussell King 
358d2e539a5SRussell King 	smc91x_resources[0].parent = smc91x_res;
359d2e539a5SRussell King 	smc91x_resources[1].parent = smc91x_res;
360ced8d21cSRussell King 	smc91x_resources[2].start = d->irq_base + NEP_IRQ_SMC91X;
361ced8d21cSRussell King 	smc91x_resources[2].end = d->irq_base + NEP_IRQ_SMC91X;
3629590e898SRussell King 	d->smc91x = platform_device_register_full(&smc91x_devinfo);
3639590e898SRussell King 
364ae14c2e2SRussell King 	platform_set_drvdata(dev, d);
365ae14c2e2SRussell King 
366ae14c2e2SRussell King 	return 0;
367ae14c2e2SRussell King 
368ced8d21cSRussell King  err_irq_alloc:
369f942b0fdSRussell King  err_id:
370f942b0fdSRussell King 	iounmap(d->base);
371f942b0fdSRussell King  err_ioremap:
372ced8d21cSRussell King 	kfree(d);
373ae14c2e2SRussell King  err_alloc:
374ae14c2e2SRussell King 	return ret;
375ae14c2e2SRussell King }
376ae14c2e2SRussell King 
377351a102dSGreg Kroah-Hartman static int neponset_remove(struct platform_device *dev)
378ae14c2e2SRussell King {
379ae14c2e2SRussell King 	struct neponset_drvdata *d = platform_get_drvdata(dev);
380b6bdfcf5SRussell King 	int irq = platform_get_irq(dev, 0);
381ae14c2e2SRussell King 
3829590e898SRussell King 	if (!IS_ERR(d->sa1111))
3839590e898SRussell King 		platform_device_unregister(d->sa1111);
3849590e898SRussell King 	if (!IS_ERR(d->smc91x))
3859590e898SRussell King 		platform_device_unregister(d->smc91x);
386b6bdfcf5SRussell King 	irq_set_chained_handler(irq, NULL);
387ced8d21cSRussell King 	irq_free_descs(d->irq_base, NEP_IRQ_NR);
388f942b0fdSRussell King 	nep_base = NULL;
389f942b0fdSRussell King 	iounmap(d->base);
390ae14c2e2SRussell King 	kfree(d);
391ae14c2e2SRussell King 
3921da177e4SLinus Torvalds 	return 0;
3931da177e4SLinus Torvalds }
3941da177e4SLinus Torvalds 
39551f93390SRussell King #ifdef CONFIG_PM_SLEEP
39651f93390SRussell King static int neponset_suspend(struct device *dev)
3971da177e4SLinus Torvalds {
39851f93390SRussell King 	struct neponset_drvdata *d = dev_get_drvdata(dev);
399ae14c2e2SRussell King 
400f942b0fdSRussell King 	d->ncr0 = readb_relaxed(d->base + NCR_0);
401f942b0fdSRussell King 	d->mdm_ctl_0 = readb_relaxed(d->base + MDM_CTL_0);
4021da177e4SLinus Torvalds 
4031da177e4SLinus Torvalds 	return 0;
4041da177e4SLinus Torvalds }
4051da177e4SLinus Torvalds 
40651f93390SRussell King static int neponset_resume(struct device *dev)
4071da177e4SLinus Torvalds {
40851f93390SRussell King 	struct neponset_drvdata *d = dev_get_drvdata(dev);
409ae14c2e2SRussell King 
410f942b0fdSRussell King 	writeb_relaxed(d->ncr0, d->base + NCR_0);
411f942b0fdSRussell King 	writeb_relaxed(d->mdm_ctl_0, d->base + MDM_CTL_0);
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds 	return 0;
4141da177e4SLinus Torvalds }
4151da177e4SLinus Torvalds 
41651f93390SRussell King static const struct dev_pm_ops neponset_pm_ops = {
41751f93390SRussell King 	.suspend_noirq = neponset_suspend,
41851f93390SRussell King 	.resume_noirq = neponset_resume,
41951f93390SRussell King 	.freeze_noirq = neponset_suspend,
42051f93390SRussell King 	.restore_noirq = neponset_resume,
42151f93390SRussell King };
42251f93390SRussell King #define PM_OPS &neponset_pm_ops
4231da177e4SLinus Torvalds #else
42451f93390SRussell King #define PM_OPS NULL
4251da177e4SLinus Torvalds #endif
4261da177e4SLinus Torvalds 
4273ae5eaecSRussell King static struct platform_driver neponset_device_driver = {
4281da177e4SLinus Torvalds 	.probe		= neponset_probe,
429351a102dSGreg Kroah-Hartman 	.remove		= neponset_remove,
4303ae5eaecSRussell King 	.driver		= {
4313ae5eaecSRussell King 		.name	= "neponset",
43251f93390SRussell King 		.pm	= PM_OPS,
4333ae5eaecSRussell King 	},
4341da177e4SLinus Torvalds };
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds static int __init neponset_init(void)
4371da177e4SLinus Torvalds {
438bab50a35SRussell King 	return platform_driver_register(&neponset_device_driver);
4391da177e4SLinus Torvalds }
4401da177e4SLinus Torvalds 
4411da177e4SLinus Torvalds subsys_initcall(neponset_init);
442