Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 | // SPDX-License-Identifier: GPL-2.0-only /* * Support code for the SCOOP interface found on various Sharp PDAs * * Copyright (c) 2004 Richard Purdie * * Based on code written by Sharp/Lineo for 2.4 kernels */ #include <linux/device.h> #include <linux/gpio/driver.h> #include <linux/string.h> #include <linux/slab.h> #include <linux/platform_device.h> #include <linux/export.h> #include <linux/io.h> #include <asm/hardware/scoop.h> /* PCMCIA to Scoop linkage There is no easy way to link multiple scoop devices into one single entity for the pxa2xx_pcmcia device so this structure is used which is setup by the platform code. This file is never modular so this symbol is always accessile to the board support files. */ struct scoop_pcmcia_config *platform_scoop_config; EXPORT_SYMBOL(platform_scoop_config); struct scoop_dev { void __iomem *base; struct gpio_chip gpio; spinlock_t scoop_lock; unsigned short suspend_clr; unsigned short suspend_set; u32 scoop_gpwr; }; void reset_scoop(struct device *dev) { struct scoop_dev *sdev = dev_get_drvdata(dev); iowrite16(0x0100, sdev->base + SCOOP_MCR); /* 00 */ iowrite16(0x0000, sdev->base + SCOOP_CDR); /* 04 */ iowrite16(0x0000, sdev->base + SCOOP_CCR); /* 10 */ iowrite16(0x0000, sdev->base + SCOOP_IMR); /* 18 */ iowrite16(0x00FF, sdev->base + SCOOP_IRM); /* 14 */ iowrite16(0x0000, sdev->base + SCOOP_ISR); /* 1C */ iowrite16(0x0000, sdev->base + SCOOP_IRM); } static void __scoop_gpio_set(struct scoop_dev *sdev, unsigned offset, int value) { unsigned short gpwr; gpwr = ioread16(sdev->base + SCOOP_GPWR); if (value) gpwr |= 1 << (offset + 1); else gpwr &= ~(1 << (offset + 1)); iowrite16(gpwr, sdev->base + SCOOP_GPWR); } static void scoop_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { struct scoop_dev *sdev = gpiochip_get_data(chip); unsigned long flags; spin_lock_irqsave(&sdev->scoop_lock, flags); __scoop_gpio_set(sdev, offset, value); spin_unlock_irqrestore(&sdev->scoop_lock, flags); } static int scoop_gpio_get(struct gpio_chip *chip, unsigned offset) { struct scoop_dev *sdev = gpiochip_get_data(chip); /* XXX: I'm unsure, but it seems so */ return !!(ioread16(sdev->base + SCOOP_GPRR) & (1 << (offset + 1))); } static int scoop_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { struct scoop_dev *sdev = gpiochip_get_data(chip); unsigned long flags; unsigned short gpcr; spin_lock_irqsave(&sdev->scoop_lock, flags); gpcr = ioread16(sdev->base + SCOOP_GPCR); gpcr &= ~(1 << (offset + 1)); iowrite16(gpcr, sdev->base + SCOOP_GPCR); spin_unlock_irqrestore(&sdev->scoop_lock, flags); return 0; } static int scoop_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { struct scoop_dev *sdev = gpiochip_get_data(chip); unsigned long flags; unsigned short gpcr; spin_lock_irqsave(&sdev->scoop_lock, flags); __scoop_gpio_set(sdev, offset, value); gpcr = ioread16(sdev->base + SCOOP_GPCR); gpcr |= 1 << (offset + 1); iowrite16(gpcr, sdev->base + SCOOP_GPCR); spin_unlock_irqrestore(&sdev->scoop_lock, flags); return 0; } unsigned short read_scoop_reg(struct device *dev, unsigned short reg) { struct scoop_dev *sdev = dev_get_drvdata(dev); return ioread16(sdev->base + reg); } void write_scoop_reg(struct device *dev, unsigned short reg, unsigned short data) { struct scoop_dev *sdev = dev_get_drvdata(dev); iowrite16(data, sdev->base + reg); } EXPORT_SYMBOL(reset_scoop); EXPORT_SYMBOL(read_scoop_reg); EXPORT_SYMBOL(write_scoop_reg); #ifdef CONFIG_PM static void check_scoop_reg(struct scoop_dev *sdev) { unsigned short mcr; mcr = ioread16(sdev->base + SCOOP_MCR); if ((mcr & 0x100) == 0) iowrite16(0x0101, sdev->base + SCOOP_MCR); } static int scoop_suspend(struct platform_device *dev, pm_message_t state) { struct scoop_dev *sdev = platform_get_drvdata(dev); check_scoop_reg(sdev); sdev->scoop_gpwr = ioread16(sdev->base + SCOOP_GPWR); iowrite16((sdev->scoop_gpwr & ~sdev->suspend_clr) | sdev->suspend_set, sdev->base + SCOOP_GPWR); return 0; } static int scoop_resume(struct platform_device *dev) { struct scoop_dev *sdev = platform_get_drvdata(dev); check_scoop_reg(sdev); iowrite16(sdev->scoop_gpwr, sdev->base + SCOOP_GPWR); return 0; } #else #define scoop_suspend NULL #define scoop_resume NULL #endif static int scoop_probe(struct platform_device *pdev) { struct scoop_dev *devptr; struct scoop_config *inf; struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); int ret; if (!mem) return -EINVAL; devptr = kzalloc(sizeof(struct scoop_dev), GFP_KERNEL); if (!devptr) return -ENOMEM; spin_lock_init(&devptr->scoop_lock); inf = pdev->dev.platform_data; devptr->base = ioremap(mem->start, resource_size(mem)); if (!devptr->base) { ret = -ENOMEM; goto err_ioremap; } platform_set_drvdata(pdev, devptr); printk("Sharp Scoop Device found at 0x%08x -> 0x%8p\n",(unsigned int)mem->start, devptr->base); iowrite16(0x0140, devptr->base + SCOOP_MCR); reset_scoop(&pdev->dev); iowrite16(0x0000, devptr->base + SCOOP_CPR); iowrite16(inf->io_dir & 0xffff, devptr->base + SCOOP_GPCR); iowrite16(inf->io_out & 0xffff, devptr->base + SCOOP_GPWR); devptr->suspend_clr = inf->suspend_clr; devptr->suspend_set = inf->suspend_set; devptr->gpio.base = -1; if (inf->gpio_base != 0) { devptr->gpio.label = dev_name(&pdev->dev); devptr->gpio.base = inf->gpio_base; devptr->gpio.ngpio = 12; /* PA11 = 0, PA12 = 1, etc. up to PA22 = 11 */ devptr->gpio.set = scoop_gpio_set; devptr->gpio.get = scoop_gpio_get; devptr->gpio.direction_input = scoop_gpio_direction_input; devptr->gpio.direction_output = scoop_gpio_direction_output; ret = gpiochip_add_data(&devptr->gpio, devptr); if (ret) goto err_gpio; } return 0; err_gpio: platform_set_drvdata(pdev, NULL); err_ioremap: iounmap(devptr->base); kfree(devptr); return ret; } static void scoop_remove(struct platform_device *pdev) { struct scoop_dev *sdev = platform_get_drvdata(pdev); if (sdev->gpio.base != -1) gpiochip_remove(&sdev->gpio); platform_set_drvdata(pdev, NULL); iounmap(sdev->base); kfree(sdev); } static struct platform_driver scoop_driver = { .probe = scoop_probe, .remove_new = scoop_remove, .suspend = scoop_suspend, .resume = scoop_resume, .driver = { .name = "sharp-scoop", }, }; static int __init scoop_init(void) { return platform_driver_register(&scoop_driver); } subsys_initcall(scoop_init); |