1 /* 2 * CLPS711X GPIO driver 3 * 4 * Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 */ 11 12 #include <linux/io.h> 13 #include <linux/slab.h> 14 #include <linux/gpio.h> 15 #include <linux/module.h> 16 #include <linux/spinlock.h> 17 #include <linux/platform_device.h> 18 19 #include <mach/hardware.h> 20 21 #define CLPS711X_GPIO_PORTS 5 22 #define CLPS711X_GPIO_NAME "gpio-clps711x" 23 24 struct clps711x_gpio { 25 struct gpio_chip chip[CLPS711X_GPIO_PORTS]; 26 spinlock_t lock; 27 }; 28 29 static void __iomem *clps711x_ports[] = { 30 CLPS711X_VIRT_BASE + PADR, 31 CLPS711X_VIRT_BASE + PBDR, 32 CLPS711X_VIRT_BASE + PCDR, 33 CLPS711X_VIRT_BASE + PDDR, 34 CLPS711X_VIRT_BASE + PEDR, 35 }; 36 37 static void __iomem *clps711x_pdirs[] = { 38 CLPS711X_VIRT_BASE + PADDR, 39 CLPS711X_VIRT_BASE + PBDDR, 40 CLPS711X_VIRT_BASE + PCDDR, 41 CLPS711X_VIRT_BASE + PDDDR, 42 CLPS711X_VIRT_BASE + PEDDR, 43 }; 44 45 #define clps711x_port(x) clps711x_ports[x->base / 8] 46 #define clps711x_pdir(x) clps711x_pdirs[x->base / 8] 47 48 static int gpio_clps711x_get(struct gpio_chip *chip, unsigned offset) 49 { 50 return !!(readb(clps711x_port(chip)) & (1 << offset)); 51 } 52 53 static void gpio_clps711x_set(struct gpio_chip *chip, unsigned offset, 54 int value) 55 { 56 int tmp; 57 unsigned long flags; 58 struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); 59 60 spin_lock_irqsave(&gpio->lock, flags); 61 tmp = readb(clps711x_port(chip)) & ~(1 << offset); 62 if (value) 63 tmp |= 1 << offset; 64 writeb(tmp, clps711x_port(chip)); 65 spin_unlock_irqrestore(&gpio->lock, flags); 66 } 67 68 static int gpio_clps711x_dir_in(struct gpio_chip *chip, unsigned offset) 69 { 70 int tmp; 71 unsigned long flags; 72 struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); 73 74 spin_lock_irqsave(&gpio->lock, flags); 75 tmp = readb(clps711x_pdir(chip)) & ~(1 << offset); 76 writeb(tmp, clps711x_pdir(chip)); 77 spin_unlock_irqrestore(&gpio->lock, flags); 78 79 return 0; 80 } 81 82 static int gpio_clps711x_dir_out(struct gpio_chip *chip, unsigned offset, 83 int value) 84 { 85 int tmp; 86 unsigned long flags; 87 struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); 88 89 spin_lock_irqsave(&gpio->lock, flags); 90 tmp = readb(clps711x_pdir(chip)) | (1 << offset); 91 writeb(tmp, clps711x_pdir(chip)); 92 tmp = readb(clps711x_port(chip)) & ~(1 << offset); 93 if (value) 94 tmp |= 1 << offset; 95 writeb(tmp, clps711x_port(chip)); 96 spin_unlock_irqrestore(&gpio->lock, flags); 97 98 return 0; 99 } 100 101 static int gpio_clps711x_dir_in_inv(struct gpio_chip *chip, unsigned offset) 102 { 103 int tmp; 104 unsigned long flags; 105 struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); 106 107 spin_lock_irqsave(&gpio->lock, flags); 108 tmp = readb(clps711x_pdir(chip)) | (1 << offset); 109 writeb(tmp, clps711x_pdir(chip)); 110 spin_unlock_irqrestore(&gpio->lock, flags); 111 112 return 0; 113 } 114 115 static int gpio_clps711x_dir_out_inv(struct gpio_chip *chip, unsigned offset, 116 int value) 117 { 118 int tmp; 119 unsigned long flags; 120 struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); 121 122 spin_lock_irqsave(&gpio->lock, flags); 123 tmp = readb(clps711x_pdir(chip)) & ~(1 << offset); 124 writeb(tmp, clps711x_pdir(chip)); 125 tmp = readb(clps711x_port(chip)) & ~(1 << offset); 126 if (value) 127 tmp |= 1 << offset; 128 writeb(tmp, clps711x_port(chip)); 129 spin_unlock_irqrestore(&gpio->lock, flags); 130 131 return 0; 132 } 133 134 static struct { 135 char *name; 136 int nr; 137 int inv_dir; 138 } clps711x_gpio_ports[] __initconst = { 139 { "PORTA", 8, 0, }, 140 { "PORTB", 8, 0, }, 141 { "PORTC", 8, 0, }, 142 { "PORTD", 8, 1, }, 143 { "PORTE", 3, 0, }, 144 }; 145 146 static int __init gpio_clps711x_init(void) 147 { 148 int i; 149 struct platform_device *pdev; 150 struct clps711x_gpio *gpio; 151 152 pdev = platform_device_alloc(CLPS711X_GPIO_NAME, 0); 153 if (!pdev) { 154 pr_err("Cannot create platform device: %s\n", 155 CLPS711X_GPIO_NAME); 156 return -ENOMEM; 157 } 158 159 platform_device_add(pdev); 160 161 gpio = devm_kzalloc(&pdev->dev, sizeof(struct clps711x_gpio), 162 GFP_KERNEL); 163 if (!gpio) { 164 dev_err(&pdev->dev, "GPIO allocating memory error\n"); 165 platform_device_unregister(pdev); 166 return -ENOMEM; 167 } 168 169 platform_set_drvdata(pdev, gpio); 170 171 spin_lock_init(&gpio->lock); 172 173 for (i = 0; i < CLPS711X_GPIO_PORTS; i++) { 174 gpio->chip[i].owner = THIS_MODULE; 175 gpio->chip[i].dev = &pdev->dev; 176 gpio->chip[i].label = clps711x_gpio_ports[i].name; 177 gpio->chip[i].base = i * 8; 178 gpio->chip[i].ngpio = clps711x_gpio_ports[i].nr; 179 gpio->chip[i].get = gpio_clps711x_get; 180 gpio->chip[i].set = gpio_clps711x_set; 181 if (!clps711x_gpio_ports[i].inv_dir) { 182 gpio->chip[i].direction_input = gpio_clps711x_dir_in; 183 gpio->chip[i].direction_output = gpio_clps711x_dir_out; 184 } else { 185 gpio->chip[i].direction_input = gpio_clps711x_dir_in_inv; 186 gpio->chip[i].direction_output = gpio_clps711x_dir_out_inv; 187 } 188 WARN_ON(gpiochip_add(&gpio->chip[i])); 189 } 190 191 dev_info(&pdev->dev, "GPIO driver initialized\n"); 192 193 return 0; 194 } 195 arch_initcall(gpio_clps711x_init); 196 197 MODULE_LICENSE("GPL v2"); 198 MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); 199 MODULE_DESCRIPTION("CLPS711X GPIO driver"); 200