1 /* 2 * GPIO interface for Intel Poulsbo SCH 3 * 4 * Copyright (c) 2010 CompuLab Ltd 5 * Author: Denis Turischev <denis@compulab.co.il> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License 2 as published 9 * by the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; see the file COPYING. If not, write to 18 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 19 */ 20 21 #include <linux/init.h> 22 #include <linux/kernel.h> 23 #include <linux/module.h> 24 #include <linux/io.h> 25 #include <linux/errno.h> 26 #include <linux/acpi.h> 27 #include <linux/platform_device.h> 28 #include <linux/pci_ids.h> 29 30 #include <linux/gpio.h> 31 32 static DEFINE_SPINLOCK(gpio_lock); 33 34 #define CGEN (0x00) 35 #define CGIO (0x04) 36 #define CGLV (0x08) 37 38 #define RGEN (0x20) 39 #define RGIO (0x24) 40 #define RGLV (0x28) 41 42 static unsigned short gpio_ba; 43 44 static int sch_gpio_core_direction_in(struct gpio_chip *gc, unsigned gpio_num) 45 { 46 u8 curr_dirs; 47 unsigned short offset, bit; 48 49 spin_lock(&gpio_lock); 50 51 offset = CGIO + gpio_num / 8; 52 bit = gpio_num % 8; 53 54 curr_dirs = inb(gpio_ba + offset); 55 56 if (!(curr_dirs & (1 << bit))) 57 outb(curr_dirs | (1 << bit), gpio_ba + offset); 58 59 spin_unlock(&gpio_lock); 60 return 0; 61 } 62 63 static int sch_gpio_core_get(struct gpio_chip *gc, unsigned gpio_num) 64 { 65 int res; 66 unsigned short offset, bit; 67 68 offset = CGLV + gpio_num / 8; 69 bit = gpio_num % 8; 70 71 res = !!(inb(gpio_ba + offset) & (1 << bit)); 72 return res; 73 } 74 75 static void sch_gpio_core_set(struct gpio_chip *gc, unsigned gpio_num, int val) 76 { 77 u8 curr_vals; 78 unsigned short offset, bit; 79 80 spin_lock(&gpio_lock); 81 82 offset = CGLV + gpio_num / 8; 83 bit = gpio_num % 8; 84 85 curr_vals = inb(gpio_ba + offset); 86 87 if (val) 88 outb(curr_vals | (1 << bit), gpio_ba + offset); 89 else 90 outb((curr_vals & ~(1 << bit)), gpio_ba + offset); 91 spin_unlock(&gpio_lock); 92 } 93 94 static int sch_gpio_core_direction_out(struct gpio_chip *gc, 95 unsigned gpio_num, int val) 96 { 97 u8 curr_dirs; 98 unsigned short offset, bit; 99 100 spin_lock(&gpio_lock); 101 102 offset = CGIO + gpio_num / 8; 103 bit = gpio_num % 8; 104 105 curr_dirs = inb(gpio_ba + offset); 106 if (curr_dirs & (1 << bit)) 107 outb(curr_dirs & ~(1 << bit), gpio_ba + offset); 108 109 spin_unlock(&gpio_lock); 110 111 /* 112 * according to the datasheet, writing to the level register has no 113 * effect when GPIO is programmed as input. 114 * Actually the the level register is read-only when configured as input. 115 * Thus presetting the output level before switching to output is _NOT_ possible. 116 * Hence we set the level after configuring the GPIO as output. 117 * But we cannot prevent a short low pulse if direction is set to high 118 * and an external pull-up is connected. 119 */ 120 sch_gpio_core_set(gc, gpio_num, val); 121 return 0; 122 } 123 124 static struct gpio_chip sch_gpio_core = { 125 .label = "sch_gpio_core", 126 .owner = THIS_MODULE, 127 .direction_input = sch_gpio_core_direction_in, 128 .get = sch_gpio_core_get, 129 .direction_output = sch_gpio_core_direction_out, 130 .set = sch_gpio_core_set, 131 }; 132 133 static int sch_gpio_resume_direction_in(struct gpio_chip *gc, 134 unsigned gpio_num) 135 { 136 u8 curr_dirs; 137 unsigned short offset, bit; 138 139 spin_lock(&gpio_lock); 140 141 offset = RGIO + gpio_num / 8; 142 bit = gpio_num % 8; 143 144 curr_dirs = inb(gpio_ba + offset); 145 146 if (!(curr_dirs & (1 << bit))) 147 outb(curr_dirs | (1 << bit), gpio_ba + offset); 148 149 spin_unlock(&gpio_lock); 150 return 0; 151 } 152 153 static int sch_gpio_resume_get(struct gpio_chip *gc, unsigned gpio_num) 154 { 155 unsigned short offset, bit; 156 157 offset = RGLV + gpio_num / 8; 158 bit = gpio_num % 8; 159 160 return !!(inb(gpio_ba + offset) & (1 << bit)); 161 } 162 163 static void sch_gpio_resume_set(struct gpio_chip *gc, 164 unsigned gpio_num, int val) 165 { 166 u8 curr_vals; 167 unsigned short offset, bit; 168 169 spin_lock(&gpio_lock); 170 171 offset = RGLV + gpio_num / 8; 172 bit = gpio_num % 8; 173 174 curr_vals = inb(gpio_ba + offset); 175 176 if (val) 177 outb(curr_vals | (1 << bit), gpio_ba + offset); 178 else 179 outb((curr_vals & ~(1 << bit)), gpio_ba + offset); 180 181 spin_unlock(&gpio_lock); 182 } 183 184 static int sch_gpio_resume_direction_out(struct gpio_chip *gc, 185 unsigned gpio_num, int val) 186 { 187 u8 curr_dirs; 188 unsigned short offset, bit; 189 190 offset = RGIO + gpio_num / 8; 191 bit = gpio_num % 8; 192 193 spin_lock(&gpio_lock); 194 195 curr_dirs = inb(gpio_ba + offset); 196 if (curr_dirs & (1 << bit)) 197 outb(curr_dirs & ~(1 << bit), gpio_ba + offset); 198 199 spin_unlock(&gpio_lock); 200 201 /* 202 * according to the datasheet, writing to the level register has no 203 * effect when GPIO is programmed as input. 204 * Actually the the level register is read-only when configured as input. 205 * Thus presetting the output level before switching to output is _NOT_ possible. 206 * Hence we set the level after configuring the GPIO as output. 207 * But we cannot prevent a short low pulse if direction is set to high 208 * and an external pull-up is connected. 209 */ 210 sch_gpio_resume_set(gc, gpio_num, val); 211 return 0; 212 } 213 214 static struct gpio_chip sch_gpio_resume = { 215 .label = "sch_gpio_resume", 216 .owner = THIS_MODULE, 217 .direction_input = sch_gpio_resume_direction_in, 218 .get = sch_gpio_resume_get, 219 .direction_output = sch_gpio_resume_direction_out, 220 .set = sch_gpio_resume_set, 221 }; 222 223 static int sch_gpio_probe(struct platform_device *pdev) 224 { 225 struct resource *res; 226 int err, id; 227 228 id = pdev->id; 229 if (!id) 230 return -ENODEV; 231 232 res = platform_get_resource(pdev, IORESOURCE_IO, 0); 233 if (!res) 234 return -EBUSY; 235 236 if (!request_region(res->start, resource_size(res), pdev->name)) 237 return -EBUSY; 238 239 gpio_ba = res->start; 240 241 switch (id) { 242 case PCI_DEVICE_ID_INTEL_SCH_LPC: 243 sch_gpio_core.base = 0; 244 sch_gpio_core.ngpio = 10; 245 sch_gpio_resume.base = 10; 246 sch_gpio_resume.ngpio = 4; 247 /* 248 * GPIO[6:0] enabled by default 249 * GPIO7 is configured by the CMC as SLPIOVR 250 * Enable GPIO[9:8] core powered gpios explicitly 251 */ 252 outb(0x3, gpio_ba + CGEN + 1); 253 /* 254 * SUS_GPIO[2:0] enabled by default 255 * Enable SUS_GPIO3 resume powered gpio explicitly 256 */ 257 outb(0x8, gpio_ba + RGEN); 258 break; 259 260 case PCI_DEVICE_ID_INTEL_ITC_LPC: 261 sch_gpio_core.base = 0; 262 sch_gpio_core.ngpio = 5; 263 sch_gpio_resume.base = 5; 264 sch_gpio_resume.ngpio = 9; 265 break; 266 267 case PCI_DEVICE_ID_INTEL_CENTERTON_ILB: 268 sch_gpio_core.base = 0; 269 sch_gpio_core.ngpio = 21; 270 sch_gpio_resume.base = 21; 271 sch_gpio_resume.ngpio = 9; 272 break; 273 274 default: 275 err = -ENODEV; 276 goto err_sch_gpio_core; 277 } 278 279 sch_gpio_core.dev = &pdev->dev; 280 sch_gpio_resume.dev = &pdev->dev; 281 282 err = gpiochip_add(&sch_gpio_core); 283 if (err < 0) 284 goto err_sch_gpio_core; 285 286 err = gpiochip_add(&sch_gpio_resume); 287 if (err < 0) 288 goto err_sch_gpio_resume; 289 290 return 0; 291 292 err_sch_gpio_resume: 293 gpiochip_remove(&sch_gpio_core); 294 295 err_sch_gpio_core: 296 release_region(res->start, resource_size(res)); 297 gpio_ba = 0; 298 299 return err; 300 } 301 302 static int sch_gpio_remove(struct platform_device *pdev) 303 { 304 struct resource *res; 305 if (gpio_ba) { 306 307 gpiochip_remove(&sch_gpio_core); 308 gpiochip_remove(&sch_gpio_resume); 309 310 res = platform_get_resource(pdev, IORESOURCE_IO, 0); 311 312 release_region(res->start, resource_size(res)); 313 gpio_ba = 0; 314 } 315 316 return 0; 317 } 318 319 static struct platform_driver sch_gpio_driver = { 320 .driver = { 321 .name = "sch_gpio", 322 .owner = THIS_MODULE, 323 }, 324 .probe = sch_gpio_probe, 325 .remove = sch_gpio_remove, 326 }; 327 328 module_platform_driver(sch_gpio_driver); 329 330 MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>"); 331 MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH"); 332 MODULE_LICENSE("GPL"); 333 MODULE_ALIAS("platform:sch_gpio"); 334