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