xref: /linux/arch/arm/common/scoop.c (revision 351a102dbf489d0e9c9b0883f76e2a94d895503d)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * Support code for the SCOOP interface found on various Sharp PDAs
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (c) 2004 Richard Purdie
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *	Based on code written by Sharp/Lineo for 2.4 kernels
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or modify
91da177e4SLinus Torvalds  * it under the terms of the GNU General Public License version 2 as
101da177e4SLinus Torvalds  * published by the Free Software Foundation.
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  */
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds #include <linux/device.h>
152f8163baSRussell King #include <linux/gpio.h>
164e57b681STim Schmielau #include <linux/string.h>
17de25968cSTim Schmielau #include <linux/slab.h>
18d052d1beSRussell King #include <linux/platform_device.h>
19dc28094bSPaul Gortmaker #include <linux/export.h>
20fced80c7SRussell King #include <linux/io.h>
211da177e4SLinus Torvalds #include <asm/hardware/scoop.h>
221da177e4SLinus Torvalds 
237ea3bbbcSRichard Purdie /* PCMCIA to Scoop linkage
247ea3bbbcSRichard Purdie 
257ea3bbbcSRichard Purdie    There is no easy way to link multiple scoop devices into one
267ea3bbbcSRichard Purdie    single entity for the pxa2xx_pcmcia device so this structure
277ea3bbbcSRichard Purdie    is used which is setup by the platform code.
287ea3bbbcSRichard Purdie 
297ea3bbbcSRichard Purdie    This file is never modular so this symbol is always
307ea3bbbcSRichard Purdie    accessile to the board support files.
317ea3bbbcSRichard Purdie */
327ea3bbbcSRichard Purdie struct scoop_pcmcia_config *platform_scoop_config;
337ea3bbbcSRichard Purdie EXPORT_SYMBOL(platform_scoop_config);
347ea3bbbcSRichard Purdie 
351da177e4SLinus Torvalds struct  scoop_dev {
362f8c5149SDmitry Baryshkov 	void __iomem *base;
37b43a9e60SDmitry Baryshkov 	struct gpio_chip gpio;
381da177e4SLinus Torvalds 	spinlock_t scoop_lock;
397c398988SRichard Purdie 	unsigned short suspend_clr;
407c398988SRichard Purdie 	unsigned short suspend_set;
411da177e4SLinus Torvalds 	u32 scoop_gpwr;
421da177e4SLinus Torvalds };
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds void reset_scoop(struct device *dev)
451da177e4SLinus Torvalds {
461da177e4SLinus Torvalds 	struct scoop_dev *sdev = dev_get_drvdata(dev);
471da177e4SLinus Torvalds 
48aa88bc0aSH Hartley Sweeten 	iowrite16(0x0100, sdev->base + SCOOP_MCR);  /* 00 */
49aa88bc0aSH Hartley Sweeten 	iowrite16(0x0000, sdev->base + SCOOP_CDR);  /* 04 */
50aa88bc0aSH Hartley Sweeten 	iowrite16(0x0000, sdev->base + SCOOP_CCR);  /* 10 */
51aa88bc0aSH Hartley Sweeten 	iowrite16(0x0000, sdev->base + SCOOP_IMR);  /* 18 */
52aa88bc0aSH Hartley Sweeten 	iowrite16(0x00FF, sdev->base + SCOOP_IRM);  /* 14 */
53aa88bc0aSH Hartley Sweeten 	iowrite16(0x0000, sdev->base + SCOOP_ISR);  /* 1C */
54c353faa4SDmitry Baryshkov 	iowrite16(0x0000, sdev->base + SCOOP_IRM);
551da177e4SLinus Torvalds }
561da177e4SLinus Torvalds 
57b43a9e60SDmitry Baryshkov static void __scoop_gpio_set(struct scoop_dev *sdev,
58b43a9e60SDmitry Baryshkov 			unsigned offset, int value)
59b43a9e60SDmitry Baryshkov {
60b43a9e60SDmitry Baryshkov 	unsigned short gpwr;
61b43a9e60SDmitry Baryshkov 
62b43a9e60SDmitry Baryshkov 	gpwr = ioread16(sdev->base + SCOOP_GPWR);
63b43a9e60SDmitry Baryshkov 	if (value)
64b43a9e60SDmitry Baryshkov 		gpwr |= 1 << (offset + 1);
65b43a9e60SDmitry Baryshkov 	else
66b43a9e60SDmitry Baryshkov 		gpwr &= ~(1 << (offset + 1));
67b43a9e60SDmitry Baryshkov 	iowrite16(gpwr, sdev->base + SCOOP_GPWR);
68b43a9e60SDmitry Baryshkov }
69b43a9e60SDmitry Baryshkov 
70b43a9e60SDmitry Baryshkov static void scoop_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
71b43a9e60SDmitry Baryshkov {
72b43a9e60SDmitry Baryshkov 	struct scoop_dev *sdev = container_of(chip, struct scoop_dev, gpio);
73b43a9e60SDmitry Baryshkov 	unsigned long flags;
74b43a9e60SDmitry Baryshkov 
75b43a9e60SDmitry Baryshkov 	spin_lock_irqsave(&sdev->scoop_lock, flags);
76b43a9e60SDmitry Baryshkov 
77b43a9e60SDmitry Baryshkov 	__scoop_gpio_set(sdev, offset, value);
78b43a9e60SDmitry Baryshkov 
79b43a9e60SDmitry Baryshkov 	spin_unlock_irqrestore(&sdev->scoop_lock, flags);
80b43a9e60SDmitry Baryshkov }
81b43a9e60SDmitry Baryshkov 
82b43a9e60SDmitry Baryshkov static int scoop_gpio_get(struct gpio_chip *chip, unsigned offset)
83b43a9e60SDmitry Baryshkov {
84b43a9e60SDmitry Baryshkov 	struct scoop_dev *sdev = container_of(chip, struct scoop_dev, gpio);
85b43a9e60SDmitry Baryshkov 
86af901ca1SAndré Goddard Rosa 	/* XXX: I'm unsure, but it seems so */
87b43a9e60SDmitry Baryshkov 	return ioread16(sdev->base + SCOOP_GPRR) & (1 << (offset + 1));
88b43a9e60SDmitry Baryshkov }
89b43a9e60SDmitry Baryshkov 
90b43a9e60SDmitry Baryshkov static int scoop_gpio_direction_input(struct gpio_chip *chip,
91b43a9e60SDmitry Baryshkov 			unsigned offset)
92b43a9e60SDmitry Baryshkov {
93b43a9e60SDmitry Baryshkov 	struct scoop_dev *sdev = container_of(chip, struct scoop_dev, gpio);
94b43a9e60SDmitry Baryshkov 	unsigned long flags;
95b43a9e60SDmitry Baryshkov 	unsigned short gpcr;
96b43a9e60SDmitry Baryshkov 
97b43a9e60SDmitry Baryshkov 	spin_lock_irqsave(&sdev->scoop_lock, flags);
98b43a9e60SDmitry Baryshkov 
99b43a9e60SDmitry Baryshkov 	gpcr = ioread16(sdev->base + SCOOP_GPCR);
100b43a9e60SDmitry Baryshkov 	gpcr &= ~(1 << (offset + 1));
101b43a9e60SDmitry Baryshkov 	iowrite16(gpcr, sdev->base + SCOOP_GPCR);
102b43a9e60SDmitry Baryshkov 
103b43a9e60SDmitry Baryshkov 	spin_unlock_irqrestore(&sdev->scoop_lock, flags);
104b43a9e60SDmitry Baryshkov 
105b43a9e60SDmitry Baryshkov 	return 0;
106b43a9e60SDmitry Baryshkov }
107b43a9e60SDmitry Baryshkov 
108b43a9e60SDmitry Baryshkov static int scoop_gpio_direction_output(struct gpio_chip *chip,
109b43a9e60SDmitry Baryshkov 			unsigned offset, int value)
110b43a9e60SDmitry Baryshkov {
111b43a9e60SDmitry Baryshkov 	struct scoop_dev *sdev = container_of(chip, struct scoop_dev, gpio);
112b43a9e60SDmitry Baryshkov 	unsigned long flags;
113b43a9e60SDmitry Baryshkov 	unsigned short gpcr;
114b43a9e60SDmitry Baryshkov 
115b43a9e60SDmitry Baryshkov 	spin_lock_irqsave(&sdev->scoop_lock, flags);
116b43a9e60SDmitry Baryshkov 
117b43a9e60SDmitry Baryshkov 	__scoop_gpio_set(sdev, offset, value);
118b43a9e60SDmitry Baryshkov 
119b43a9e60SDmitry Baryshkov 	gpcr = ioread16(sdev->base + SCOOP_GPCR);
120b43a9e60SDmitry Baryshkov 	gpcr |= 1 << (offset + 1);
121b43a9e60SDmitry Baryshkov 	iowrite16(gpcr, sdev->base + SCOOP_GPCR);
122b43a9e60SDmitry Baryshkov 
123b43a9e60SDmitry Baryshkov 	spin_unlock_irqrestore(&sdev->scoop_lock, flags);
124b43a9e60SDmitry Baryshkov 
125b43a9e60SDmitry Baryshkov 	return 0;
126b43a9e60SDmitry Baryshkov }
127b43a9e60SDmitry Baryshkov 
1281da177e4SLinus Torvalds unsigned short read_scoop_reg(struct device *dev, unsigned short reg)
1291da177e4SLinus Torvalds {
1301da177e4SLinus Torvalds 	struct scoop_dev *sdev = dev_get_drvdata(dev);
131c353faa4SDmitry Baryshkov 	return ioread16(sdev->base + reg);
1321da177e4SLinus Torvalds }
1331da177e4SLinus Torvalds 
1341da177e4SLinus Torvalds void write_scoop_reg(struct device *dev, unsigned short reg, unsigned short data)
1351da177e4SLinus Torvalds {
1361da177e4SLinus Torvalds 	struct scoop_dev *sdev = dev_get_drvdata(dev);
137c353faa4SDmitry Baryshkov 	iowrite16(data, sdev->base + reg);
1381da177e4SLinus Torvalds }
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds EXPORT_SYMBOL(reset_scoop);
1411da177e4SLinus Torvalds EXPORT_SYMBOL(read_scoop_reg);
1421da177e4SLinus Torvalds EXPORT_SYMBOL(write_scoop_reg);
1431da177e4SLinus Torvalds 
144cfab57e0SStefan Schmidt #ifdef CONFIG_PM
1457c398988SRichard Purdie static void check_scoop_reg(struct scoop_dev *sdev)
1467c398988SRichard Purdie {
1477c398988SRichard Purdie 	unsigned short mcr;
1487c398988SRichard Purdie 
149c353faa4SDmitry Baryshkov 	mcr = ioread16(sdev->base + SCOOP_MCR);
1507c398988SRichard Purdie 	if ((mcr & 0x100) == 0)
151c353faa4SDmitry Baryshkov 		iowrite16(0x0101, sdev->base + SCOOP_MCR);
1527c398988SRichard Purdie }
1537c398988SRichard Purdie 
1543ae5eaecSRussell King static int scoop_suspend(struct platform_device *dev, pm_message_t state)
1551da177e4SLinus Torvalds {
1563ae5eaecSRussell King 	struct scoop_dev *sdev = platform_get_drvdata(dev);
1571da177e4SLinus Torvalds 
1587c398988SRichard Purdie 	check_scoop_reg(sdev);
159c353faa4SDmitry Baryshkov 	sdev->scoop_gpwr = ioread16(sdev->base + SCOOP_GPWR);
160c353faa4SDmitry Baryshkov 	iowrite16((sdev->scoop_gpwr & ~sdev->suspend_clr) | sdev->suspend_set, sdev->base + SCOOP_GPWR);
1619480e307SRussell King 
1621da177e4SLinus Torvalds 	return 0;
1631da177e4SLinus Torvalds }
1641da177e4SLinus Torvalds 
1653ae5eaecSRussell King static int scoop_resume(struct platform_device *dev)
1661da177e4SLinus Torvalds {
1673ae5eaecSRussell King 	struct scoop_dev *sdev = platform_get_drvdata(dev);
1681da177e4SLinus Torvalds 
1697c398988SRichard Purdie 	check_scoop_reg(sdev);
170c353faa4SDmitry Baryshkov 	iowrite16(sdev->scoop_gpwr, sdev->base + SCOOP_GPWR);
1719480e307SRussell King 
1721da177e4SLinus Torvalds 	return 0;
1731da177e4SLinus Torvalds }
1741da177e4SLinus Torvalds #else
1751da177e4SLinus Torvalds #define scoop_suspend	NULL
1761da177e4SLinus Torvalds #define scoop_resume	NULL
1771da177e4SLinus Torvalds #endif
1781da177e4SLinus Torvalds 
179*351a102dSGreg Kroah-Hartman static int scoop_probe(struct platform_device *pdev)
1801da177e4SLinus Torvalds {
1811da177e4SLinus Torvalds 	struct scoop_dev *devptr;
1821da177e4SLinus Torvalds 	struct scoop_config *inf;
1831da177e4SLinus Torvalds 	struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
184b43a9e60SDmitry Baryshkov 	int ret;
185b43a9e60SDmitry Baryshkov 	int temp;
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds 	if (!mem)
1881da177e4SLinus Torvalds 		return -EINVAL;
1891da177e4SLinus Torvalds 
190d2a02b93SRussell King 	devptr = kzalloc(sizeof(struct scoop_dev), GFP_KERNEL);
1911da177e4SLinus Torvalds 	if (!devptr)
1921da177e4SLinus Torvalds 		return -ENOMEM;
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds 	spin_lock_init(&devptr->scoop_lock);
1951da177e4SLinus Torvalds 
1963ae5eaecSRussell King 	inf = pdev->dev.platform_data;
19728f65c11SJoe Perches 	devptr->base = ioremap(mem->start, resource_size(mem));
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds 	if (!devptr->base) {
200b43a9e60SDmitry Baryshkov 		ret = -ENOMEM;
201b43a9e60SDmitry Baryshkov 		goto err_ioremap;
2021da177e4SLinus Torvalds 	}
2031da177e4SLinus Torvalds 
2043ae5eaecSRussell King 	platform_set_drvdata(pdev, devptr);
2051da177e4SLinus Torvalds 
2062f8c5149SDmitry Baryshkov 	printk("Sharp Scoop Device found at 0x%08x -> 0x%8p\n",(unsigned int)mem->start, devptr->base);
2071da177e4SLinus Torvalds 
208c353faa4SDmitry Baryshkov 	iowrite16(0x0140, devptr->base + SCOOP_MCR);
209c35bf4a5SPavel Machek 	reset_scoop(&pdev->dev);
210c353faa4SDmitry Baryshkov 	iowrite16(0x0000, devptr->base + SCOOP_CPR);
211c353faa4SDmitry Baryshkov 	iowrite16(inf->io_dir & 0xffff, devptr->base + SCOOP_GPCR);
212c353faa4SDmitry Baryshkov 	iowrite16(inf->io_out & 0xffff, devptr->base + SCOOP_GPWR);
2131da177e4SLinus Torvalds 
2147c398988SRichard Purdie 	devptr->suspend_clr = inf->suspend_clr;
2157c398988SRichard Purdie 	devptr->suspend_set = inf->suspend_set;
2167c398988SRichard Purdie 
217b43a9e60SDmitry Baryshkov 	devptr->gpio.base = -1;
218b43a9e60SDmitry Baryshkov 
219b43a9e60SDmitry Baryshkov 	if (inf->gpio_base != 0) {
2203f978704SKay Sievers 		devptr->gpio.label = dev_name(&pdev->dev);
221b43a9e60SDmitry Baryshkov 		devptr->gpio.base = inf->gpio_base;
222b43a9e60SDmitry Baryshkov 		devptr->gpio.ngpio = 12; /* PA11 = 0, PA12 = 1, etc. up to PA22 = 11 */
223b43a9e60SDmitry Baryshkov 		devptr->gpio.set = scoop_gpio_set;
224b43a9e60SDmitry Baryshkov 		devptr->gpio.get = scoop_gpio_get;
225b43a9e60SDmitry Baryshkov 		devptr->gpio.direction_input = scoop_gpio_direction_input;
226b43a9e60SDmitry Baryshkov 		devptr->gpio.direction_output = scoop_gpio_direction_output;
227b43a9e60SDmitry Baryshkov 
228b43a9e60SDmitry Baryshkov 		ret = gpiochip_add(&devptr->gpio);
229b43a9e60SDmitry Baryshkov 		if (ret)
230b43a9e60SDmitry Baryshkov 			goto err_gpio;
231b43a9e60SDmitry Baryshkov 	}
232b43a9e60SDmitry Baryshkov 
2331da177e4SLinus Torvalds 	return 0;
234b43a9e60SDmitry Baryshkov 
235b43a9e60SDmitry Baryshkov 	if (devptr->gpio.base != -1)
236b43a9e60SDmitry Baryshkov 		temp = gpiochip_remove(&devptr->gpio);
237b43a9e60SDmitry Baryshkov err_gpio:
238b43a9e60SDmitry Baryshkov 	platform_set_drvdata(pdev, NULL);
239b43a9e60SDmitry Baryshkov err_ioremap:
240b43a9e60SDmitry Baryshkov 	iounmap(devptr->base);
241b43a9e60SDmitry Baryshkov 	kfree(devptr);
242b43a9e60SDmitry Baryshkov 
243b43a9e60SDmitry Baryshkov 	return ret;
2441da177e4SLinus Torvalds }
2451da177e4SLinus Torvalds 
246*351a102dSGreg Kroah-Hartman static int scoop_remove(struct platform_device *pdev)
2471da177e4SLinus Torvalds {
2483ae5eaecSRussell King 	struct scoop_dev *sdev = platform_get_drvdata(pdev);
249b43a9e60SDmitry Baryshkov 	int ret;
250b43a9e60SDmitry Baryshkov 
251b43a9e60SDmitry Baryshkov 	if (!sdev)
252b43a9e60SDmitry Baryshkov 		return -EINVAL;
253b43a9e60SDmitry Baryshkov 
254b43a9e60SDmitry Baryshkov 	if (sdev->gpio.base != -1) {
255b43a9e60SDmitry Baryshkov 		ret = gpiochip_remove(&sdev->gpio);
256b43a9e60SDmitry Baryshkov 		if (ret) {
257b43a9e60SDmitry Baryshkov 			dev_err(&pdev->dev, "Can't remove gpio chip: %d\n", ret);
258b43a9e60SDmitry Baryshkov 			return ret;
259b43a9e60SDmitry Baryshkov 		}
260b43a9e60SDmitry Baryshkov 	}
261b43a9e60SDmitry Baryshkov 
262b43a9e60SDmitry Baryshkov 	platform_set_drvdata(pdev, NULL);
2631da177e4SLinus Torvalds 	iounmap(sdev->base);
2641da177e4SLinus Torvalds 	kfree(sdev);
265b43a9e60SDmitry Baryshkov 
2661da177e4SLinus Torvalds 	return 0;
2671da177e4SLinus Torvalds }
2681da177e4SLinus Torvalds 
2693ae5eaecSRussell King static struct platform_driver scoop_driver = {
2701da177e4SLinus Torvalds 	.probe		= scoop_probe,
271*351a102dSGreg Kroah-Hartman 	.remove		= scoop_remove,
2721da177e4SLinus Torvalds 	.suspend	= scoop_suspend,
2731da177e4SLinus Torvalds 	.resume		= scoop_resume,
2743ae5eaecSRussell King 	.driver		= {
2753ae5eaecSRussell King 		.name	= "sharp-scoop",
2763ae5eaecSRussell King 	},
2771da177e4SLinus Torvalds };
2781da177e4SLinus Torvalds 
2792f8c5149SDmitry Baryshkov static int __init scoop_init(void)
2801da177e4SLinus Torvalds {
2813ae5eaecSRussell King 	return platform_driver_register(&scoop_driver);
2821da177e4SLinus Torvalds }
2831da177e4SLinus Torvalds 
2841da177e4SLinus Torvalds subsys_initcall(scoop_init);
285