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