xref: /linux/arch/arm/common/scoop.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * Support code for the SCOOP interface found on various Sharp PDAs
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (c) 2004 Richard Purdie
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *	Based on code written by Sharp/Lineo for 2.4 kernels
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
101da177e4SLinus Torvalds #include <linux/device.h>
11f73d137dSLinus Walleij #include <linux/gpio/driver.h>
124e57b681STim Schmielau #include <linux/string.h>
13de25968cSTim Schmielau #include <linux/slab.h>
14d052d1beSRussell King #include <linux/platform_device.h>
15dc28094bSPaul Gortmaker #include <linux/export.h>
16fced80c7SRussell King #include <linux/io.h>
171da177e4SLinus Torvalds #include <asm/hardware/scoop.h>
181da177e4SLinus Torvalds 
197ea3bbbcSRichard Purdie /* PCMCIA to Scoop linkage
207ea3bbbcSRichard Purdie 
217ea3bbbcSRichard Purdie    There is no easy way to link multiple scoop devices into one
227ea3bbbcSRichard Purdie    single entity for the pxa2xx_pcmcia device so this structure
237ea3bbbcSRichard Purdie    is used which is setup by the platform code.
247ea3bbbcSRichard Purdie 
257ea3bbbcSRichard Purdie    This file is never modular so this symbol is always
267ea3bbbcSRichard Purdie    accessile to the board support files.
277ea3bbbcSRichard Purdie */
287ea3bbbcSRichard Purdie struct scoop_pcmcia_config *platform_scoop_config;
297ea3bbbcSRichard Purdie EXPORT_SYMBOL(platform_scoop_config);
307ea3bbbcSRichard Purdie 
311da177e4SLinus Torvalds struct  scoop_dev {
322f8c5149SDmitry Baryshkov 	void __iomem *base;
33b43a9e60SDmitry Baryshkov 	struct gpio_chip gpio;
341da177e4SLinus Torvalds 	spinlock_t scoop_lock;
357c398988SRichard Purdie 	unsigned short suspend_clr;
367c398988SRichard Purdie 	unsigned short suspend_set;
371da177e4SLinus Torvalds 	u32 scoop_gpwr;
381da177e4SLinus Torvalds };
391da177e4SLinus Torvalds 
reset_scoop(struct device * dev)401da177e4SLinus Torvalds void reset_scoop(struct device *dev)
411da177e4SLinus Torvalds {
421da177e4SLinus Torvalds 	struct scoop_dev *sdev = dev_get_drvdata(dev);
431da177e4SLinus Torvalds 
44aa88bc0aSH Hartley Sweeten 	iowrite16(0x0100, sdev->base + SCOOP_MCR);  /* 00 */
45aa88bc0aSH Hartley Sweeten 	iowrite16(0x0000, sdev->base + SCOOP_CDR);  /* 04 */
46aa88bc0aSH Hartley Sweeten 	iowrite16(0x0000, sdev->base + SCOOP_CCR);  /* 10 */
47aa88bc0aSH Hartley Sweeten 	iowrite16(0x0000, sdev->base + SCOOP_IMR);  /* 18 */
48aa88bc0aSH Hartley Sweeten 	iowrite16(0x00FF, sdev->base + SCOOP_IRM);  /* 14 */
49aa88bc0aSH Hartley Sweeten 	iowrite16(0x0000, sdev->base + SCOOP_ISR);  /* 1C */
50c353faa4SDmitry Baryshkov 	iowrite16(0x0000, sdev->base + SCOOP_IRM);
511da177e4SLinus Torvalds }
521da177e4SLinus Torvalds 
__scoop_gpio_set(struct scoop_dev * sdev,unsigned offset,int value)53b43a9e60SDmitry Baryshkov static void __scoop_gpio_set(struct scoop_dev *sdev,
54b43a9e60SDmitry Baryshkov 			unsigned offset, int value)
55b43a9e60SDmitry Baryshkov {
56b43a9e60SDmitry Baryshkov 	unsigned short gpwr;
57b43a9e60SDmitry Baryshkov 
58b43a9e60SDmitry Baryshkov 	gpwr = ioread16(sdev->base + SCOOP_GPWR);
59b43a9e60SDmitry Baryshkov 	if (value)
60b43a9e60SDmitry Baryshkov 		gpwr |= 1 << (offset + 1);
61b43a9e60SDmitry Baryshkov 	else
62b43a9e60SDmitry Baryshkov 		gpwr &= ~(1 << (offset + 1));
63b43a9e60SDmitry Baryshkov 	iowrite16(gpwr, sdev->base + SCOOP_GPWR);
64b43a9e60SDmitry Baryshkov }
65b43a9e60SDmitry Baryshkov 
scoop_gpio_set(struct gpio_chip * chip,unsigned offset,int value)66b43a9e60SDmitry Baryshkov static void scoop_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
67b43a9e60SDmitry Baryshkov {
68050c5429SLinus Walleij 	struct scoop_dev *sdev = gpiochip_get_data(chip);
69b43a9e60SDmitry Baryshkov 	unsigned long flags;
70b43a9e60SDmitry Baryshkov 
71b43a9e60SDmitry Baryshkov 	spin_lock_irqsave(&sdev->scoop_lock, flags);
72b43a9e60SDmitry Baryshkov 
73b43a9e60SDmitry Baryshkov 	__scoop_gpio_set(sdev, offset, value);
74b43a9e60SDmitry Baryshkov 
75b43a9e60SDmitry Baryshkov 	spin_unlock_irqrestore(&sdev->scoop_lock, flags);
76b43a9e60SDmitry Baryshkov }
77b43a9e60SDmitry Baryshkov 
scoop_gpio_get(struct gpio_chip * chip,unsigned offset)78b43a9e60SDmitry Baryshkov static int scoop_gpio_get(struct gpio_chip *chip, unsigned offset)
79b43a9e60SDmitry Baryshkov {
80050c5429SLinus Walleij 	struct scoop_dev *sdev = gpiochip_get_data(chip);
81b43a9e60SDmitry Baryshkov 
82af901ca1SAndré Goddard Rosa 	/* XXX: I'm unsure, but it seems so */
8386d5e657SLinus Walleij 	return !!(ioread16(sdev->base + SCOOP_GPRR) & (1 << (offset + 1)));
84b43a9e60SDmitry Baryshkov }
85b43a9e60SDmitry Baryshkov 
scoop_gpio_direction_input(struct gpio_chip * chip,unsigned offset)86b43a9e60SDmitry Baryshkov static int scoop_gpio_direction_input(struct gpio_chip *chip,
87b43a9e60SDmitry Baryshkov 			unsigned offset)
88b43a9e60SDmitry Baryshkov {
89050c5429SLinus Walleij 	struct scoop_dev *sdev = gpiochip_get_data(chip);
90b43a9e60SDmitry Baryshkov 	unsigned long flags;
91b43a9e60SDmitry Baryshkov 	unsigned short gpcr;
92b43a9e60SDmitry Baryshkov 
93b43a9e60SDmitry Baryshkov 	spin_lock_irqsave(&sdev->scoop_lock, flags);
94b43a9e60SDmitry Baryshkov 
95b43a9e60SDmitry Baryshkov 	gpcr = ioread16(sdev->base + SCOOP_GPCR);
96b43a9e60SDmitry Baryshkov 	gpcr &= ~(1 << (offset + 1));
97b43a9e60SDmitry Baryshkov 	iowrite16(gpcr, sdev->base + SCOOP_GPCR);
98b43a9e60SDmitry Baryshkov 
99b43a9e60SDmitry Baryshkov 	spin_unlock_irqrestore(&sdev->scoop_lock, flags);
100b43a9e60SDmitry Baryshkov 
101b43a9e60SDmitry Baryshkov 	return 0;
102b43a9e60SDmitry Baryshkov }
103b43a9e60SDmitry Baryshkov 
scoop_gpio_direction_output(struct gpio_chip * chip,unsigned offset,int value)104b43a9e60SDmitry Baryshkov static int scoop_gpio_direction_output(struct gpio_chip *chip,
105b43a9e60SDmitry Baryshkov 			unsigned offset, int value)
106b43a9e60SDmitry Baryshkov {
107050c5429SLinus Walleij 	struct scoop_dev *sdev = gpiochip_get_data(chip);
108b43a9e60SDmitry Baryshkov 	unsigned long flags;
109b43a9e60SDmitry Baryshkov 	unsigned short gpcr;
110b43a9e60SDmitry Baryshkov 
111b43a9e60SDmitry Baryshkov 	spin_lock_irqsave(&sdev->scoop_lock, flags);
112b43a9e60SDmitry Baryshkov 
113b43a9e60SDmitry Baryshkov 	__scoop_gpio_set(sdev, offset, value);
114b43a9e60SDmitry Baryshkov 
115b43a9e60SDmitry Baryshkov 	gpcr = ioread16(sdev->base + SCOOP_GPCR);
116b43a9e60SDmitry Baryshkov 	gpcr |= 1 << (offset + 1);
117b43a9e60SDmitry Baryshkov 	iowrite16(gpcr, sdev->base + SCOOP_GPCR);
118b43a9e60SDmitry Baryshkov 
119b43a9e60SDmitry Baryshkov 	spin_unlock_irqrestore(&sdev->scoop_lock, flags);
120b43a9e60SDmitry Baryshkov 
121b43a9e60SDmitry Baryshkov 	return 0;
122b43a9e60SDmitry Baryshkov }
123b43a9e60SDmitry Baryshkov 
read_scoop_reg(struct device * dev,unsigned short reg)1241da177e4SLinus Torvalds unsigned short read_scoop_reg(struct device *dev, unsigned short reg)
1251da177e4SLinus Torvalds {
1261da177e4SLinus Torvalds 	struct scoop_dev *sdev = dev_get_drvdata(dev);
127c353faa4SDmitry Baryshkov 	return ioread16(sdev->base + reg);
1281da177e4SLinus Torvalds }
1291da177e4SLinus Torvalds 
write_scoop_reg(struct device * dev,unsigned short reg,unsigned short data)1301da177e4SLinus Torvalds void write_scoop_reg(struct device *dev, unsigned short reg, unsigned short data)
1311da177e4SLinus Torvalds {
1321da177e4SLinus Torvalds 	struct scoop_dev *sdev = dev_get_drvdata(dev);
133c353faa4SDmitry Baryshkov 	iowrite16(data, sdev->base + reg);
1341da177e4SLinus Torvalds }
1351da177e4SLinus Torvalds 
1361da177e4SLinus Torvalds EXPORT_SYMBOL(reset_scoop);
1371da177e4SLinus Torvalds EXPORT_SYMBOL(read_scoop_reg);
1381da177e4SLinus Torvalds EXPORT_SYMBOL(write_scoop_reg);
1391da177e4SLinus Torvalds 
140cfab57e0SStefan Schmidt #ifdef CONFIG_PM
check_scoop_reg(struct scoop_dev * sdev)1417c398988SRichard Purdie static void check_scoop_reg(struct scoop_dev *sdev)
1427c398988SRichard Purdie {
1437c398988SRichard Purdie 	unsigned short mcr;
1447c398988SRichard Purdie 
145c353faa4SDmitry Baryshkov 	mcr = ioread16(sdev->base + SCOOP_MCR);
1467c398988SRichard Purdie 	if ((mcr & 0x100) == 0)
147c353faa4SDmitry Baryshkov 		iowrite16(0x0101, sdev->base + SCOOP_MCR);
1487c398988SRichard Purdie }
1497c398988SRichard Purdie 
scoop_suspend(struct platform_device * dev,pm_message_t state)1503ae5eaecSRussell King static int scoop_suspend(struct platform_device *dev, pm_message_t state)
1511da177e4SLinus Torvalds {
1523ae5eaecSRussell King 	struct scoop_dev *sdev = platform_get_drvdata(dev);
1531da177e4SLinus Torvalds 
1547c398988SRichard Purdie 	check_scoop_reg(sdev);
155c353faa4SDmitry Baryshkov 	sdev->scoop_gpwr = ioread16(sdev->base + SCOOP_GPWR);
156c353faa4SDmitry Baryshkov 	iowrite16((sdev->scoop_gpwr & ~sdev->suspend_clr) | sdev->suspend_set, sdev->base + SCOOP_GPWR);
1579480e307SRussell King 
1581da177e4SLinus Torvalds 	return 0;
1591da177e4SLinus Torvalds }
1601da177e4SLinus Torvalds 
scoop_resume(struct platform_device * dev)1613ae5eaecSRussell King static int scoop_resume(struct platform_device *dev)
1621da177e4SLinus Torvalds {
1633ae5eaecSRussell King 	struct scoop_dev *sdev = platform_get_drvdata(dev);
1641da177e4SLinus Torvalds 
1657c398988SRichard Purdie 	check_scoop_reg(sdev);
166c353faa4SDmitry Baryshkov 	iowrite16(sdev->scoop_gpwr, sdev->base + SCOOP_GPWR);
1679480e307SRussell King 
1681da177e4SLinus Torvalds 	return 0;
1691da177e4SLinus Torvalds }
1701da177e4SLinus Torvalds #else
1711da177e4SLinus Torvalds #define scoop_suspend	NULL
1721da177e4SLinus Torvalds #define scoop_resume	NULL
1731da177e4SLinus Torvalds #endif
1741da177e4SLinus Torvalds 
scoop_probe(struct platform_device * pdev)175351a102dSGreg Kroah-Hartman static int scoop_probe(struct platform_device *pdev)
1761da177e4SLinus Torvalds {
1771da177e4SLinus Torvalds 	struct scoop_dev *devptr;
1781da177e4SLinus Torvalds 	struct scoop_config *inf;
1791da177e4SLinus Torvalds 	struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
180b43a9e60SDmitry Baryshkov 	int ret;
1811da177e4SLinus Torvalds 
1821da177e4SLinus Torvalds 	if (!mem)
1831da177e4SLinus Torvalds 		return -EINVAL;
1841da177e4SLinus Torvalds 
185d2a02b93SRussell King 	devptr = kzalloc(sizeof(struct scoop_dev), GFP_KERNEL);
1861da177e4SLinus Torvalds 	if (!devptr)
1871da177e4SLinus Torvalds 		return -ENOMEM;
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds 	spin_lock_init(&devptr->scoop_lock);
1901da177e4SLinus Torvalds 
1913ae5eaecSRussell King 	inf = pdev->dev.platform_data;
19228f65c11SJoe Perches 	devptr->base = ioremap(mem->start, resource_size(mem));
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds 	if (!devptr->base) {
195b43a9e60SDmitry Baryshkov 		ret = -ENOMEM;
196b43a9e60SDmitry Baryshkov 		goto err_ioremap;
1971da177e4SLinus Torvalds 	}
1981da177e4SLinus Torvalds 
1993ae5eaecSRussell King 	platform_set_drvdata(pdev, devptr);
2001da177e4SLinus Torvalds 
2012f8c5149SDmitry Baryshkov 	printk("Sharp Scoop Device found at 0x%08x -> 0x%8p\n",(unsigned int)mem->start, devptr->base);
2021da177e4SLinus Torvalds 
203c353faa4SDmitry Baryshkov 	iowrite16(0x0140, devptr->base + SCOOP_MCR);
204c35bf4a5SPavel Machek 	reset_scoop(&pdev->dev);
205c353faa4SDmitry Baryshkov 	iowrite16(0x0000, devptr->base + SCOOP_CPR);
206c353faa4SDmitry Baryshkov 	iowrite16(inf->io_dir & 0xffff, devptr->base + SCOOP_GPCR);
207c353faa4SDmitry Baryshkov 	iowrite16(inf->io_out & 0xffff, devptr->base + SCOOP_GPWR);
2081da177e4SLinus Torvalds 
2097c398988SRichard Purdie 	devptr->suspend_clr = inf->suspend_clr;
2107c398988SRichard Purdie 	devptr->suspend_set = inf->suspend_set;
2117c398988SRichard Purdie 
212b43a9e60SDmitry Baryshkov 	devptr->gpio.base = -1;
213b43a9e60SDmitry Baryshkov 
214b43a9e60SDmitry Baryshkov 	if (inf->gpio_base != 0) {
2153f978704SKay Sievers 		devptr->gpio.label = dev_name(&pdev->dev);
216b43a9e60SDmitry Baryshkov 		devptr->gpio.base = inf->gpio_base;
217b43a9e60SDmitry Baryshkov 		devptr->gpio.ngpio = 12; /* PA11 = 0, PA12 = 1, etc. up to PA22 = 11 */
218b43a9e60SDmitry Baryshkov 		devptr->gpio.set = scoop_gpio_set;
219b43a9e60SDmitry Baryshkov 		devptr->gpio.get = scoop_gpio_get;
220b43a9e60SDmitry Baryshkov 		devptr->gpio.direction_input = scoop_gpio_direction_input;
221b43a9e60SDmitry Baryshkov 		devptr->gpio.direction_output = scoop_gpio_direction_output;
222b43a9e60SDmitry Baryshkov 
223050c5429SLinus Walleij 		ret = gpiochip_add_data(&devptr->gpio, devptr);
224b43a9e60SDmitry Baryshkov 		if (ret)
225b43a9e60SDmitry Baryshkov 			goto err_gpio;
226b43a9e60SDmitry Baryshkov 	}
227b43a9e60SDmitry Baryshkov 
2281da177e4SLinus Torvalds 	return 0;
229b43a9e60SDmitry Baryshkov 
230b43a9e60SDmitry Baryshkov err_gpio:
231b43a9e60SDmitry Baryshkov 	platform_set_drvdata(pdev, NULL);
232b43a9e60SDmitry Baryshkov err_ioremap:
233b43a9e60SDmitry Baryshkov 	iounmap(devptr->base);
234b43a9e60SDmitry Baryshkov 	kfree(devptr);
235b43a9e60SDmitry Baryshkov 
236b43a9e60SDmitry Baryshkov 	return ret;
2371da177e4SLinus Torvalds }
2381da177e4SLinus Torvalds 
scoop_remove(struct platform_device * pdev)239*74a5b94bSUwe Kleine-König static void scoop_remove(struct platform_device *pdev)
2401da177e4SLinus Torvalds {
2413ae5eaecSRussell King 	struct scoop_dev *sdev = platform_get_drvdata(pdev);
242b43a9e60SDmitry Baryshkov 
24388d5e520Sabdoulaye berthe 	if (sdev->gpio.base != -1)
24488d5e520Sabdoulaye berthe 		gpiochip_remove(&sdev->gpio);
245b43a9e60SDmitry Baryshkov 
246b43a9e60SDmitry Baryshkov 	platform_set_drvdata(pdev, NULL);
2471da177e4SLinus Torvalds 	iounmap(sdev->base);
2481da177e4SLinus Torvalds 	kfree(sdev);
2491da177e4SLinus Torvalds }
2501da177e4SLinus Torvalds 
2513ae5eaecSRussell King static struct platform_driver scoop_driver = {
2521da177e4SLinus Torvalds 	.probe		= scoop_probe,
253*74a5b94bSUwe Kleine-König 	.remove_new	= scoop_remove,
2541da177e4SLinus Torvalds 	.suspend	= scoop_suspend,
2551da177e4SLinus Torvalds 	.resume		= scoop_resume,
2563ae5eaecSRussell King 	.driver		= {
2573ae5eaecSRussell King 		.name	= "sharp-scoop",
2583ae5eaecSRussell King 	},
2591da177e4SLinus Torvalds };
2601da177e4SLinus Torvalds 
scoop_init(void)2612f8c5149SDmitry Baryshkov static int __init scoop_init(void)
2621da177e4SLinus Torvalds {
2633ae5eaecSRussell King 	return platform_driver_register(&scoop_driver);
2641da177e4SLinus Torvalds }
2651da177e4SLinus Torvalds 
2661da177e4SLinus Torvalds subsys_initcall(scoop_init);
267