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 sch_gpio_core_set(gc, gpio_num, val); 101 102 spin_lock(&gpio_lock); 103 104 offset = CGIO + gpio_num / 8; 105 bit = gpio_num % 8; 106 107 curr_dirs = inb(gpio_ba + offset); 108 if (curr_dirs & (1 << bit)) 109 outb(curr_dirs & ~(1 << bit), gpio_ba + offset); 110 111 spin_unlock(&gpio_lock); 112 return 0; 113 } 114 115 static struct gpio_chip sch_gpio_core = { 116 .label = "sch_gpio_core", 117 .owner = THIS_MODULE, 118 .direction_input = sch_gpio_core_direction_in, 119 .get = sch_gpio_core_get, 120 .direction_output = sch_gpio_core_direction_out, 121 .set = sch_gpio_core_set, 122 }; 123 124 static int sch_gpio_resume_direction_in(struct gpio_chip *gc, 125 unsigned gpio_num) 126 { 127 u8 curr_dirs; 128 unsigned short offset, bit; 129 130 spin_lock(&gpio_lock); 131 132 offset = RGIO + gpio_num / 8; 133 bit = gpio_num % 8; 134 135 curr_dirs = inb(gpio_ba + offset); 136 137 if (!(curr_dirs & (1 << bit))) 138 outb(curr_dirs | (1 << bit), gpio_ba + offset); 139 140 spin_unlock(&gpio_lock); 141 return 0; 142 } 143 144 static int sch_gpio_resume_get(struct gpio_chip *gc, unsigned gpio_num) 145 { 146 unsigned short offset, bit; 147 148 offset = RGLV + gpio_num / 8; 149 bit = gpio_num % 8; 150 151 return !!(inb(gpio_ba + offset) & (1 << bit)); 152 } 153 154 static void sch_gpio_resume_set(struct gpio_chip *gc, 155 unsigned gpio_num, int val) 156 { 157 u8 curr_vals; 158 unsigned short offset, bit; 159 160 spin_lock(&gpio_lock); 161 162 offset = RGLV + gpio_num / 8; 163 bit = gpio_num % 8; 164 165 curr_vals = inb(gpio_ba + offset); 166 167 if (val) 168 outb(curr_vals | (1 << bit), gpio_ba + offset); 169 else 170 outb((curr_vals & ~(1 << bit)), gpio_ba + offset); 171 172 spin_unlock(&gpio_lock); 173 } 174 175 static int sch_gpio_resume_direction_out(struct gpio_chip *gc, 176 unsigned gpio_num, int val) 177 { 178 u8 curr_dirs; 179 unsigned short offset, bit; 180 181 sch_gpio_resume_set(gc, gpio_num, val); 182 183 offset = RGIO + gpio_num / 8; 184 bit = gpio_num % 8; 185 186 spin_lock(&gpio_lock); 187 188 curr_dirs = inb(gpio_ba + offset); 189 if (curr_dirs & (1 << bit)) 190 outb(curr_dirs & ~(1 << bit), gpio_ba + offset); 191 192 spin_unlock(&gpio_lock); 193 return 0; 194 } 195 196 static struct gpio_chip sch_gpio_resume = { 197 .label = "sch_gpio_resume", 198 .owner = THIS_MODULE, 199 .direction_input = sch_gpio_resume_direction_in, 200 .get = sch_gpio_resume_get, 201 .direction_output = sch_gpio_resume_direction_out, 202 .set = sch_gpio_resume_set, 203 }; 204 205 static int sch_gpio_probe(struct platform_device *pdev) 206 { 207 struct resource *res; 208 int err, id; 209 210 id = pdev->id; 211 if (!id) 212 return -ENODEV; 213 214 res = platform_get_resource(pdev, IORESOURCE_IO, 0); 215 if (!res) 216 return -EBUSY; 217 218 if (!request_region(res->start, resource_size(res), pdev->name)) 219 return -EBUSY; 220 221 gpio_ba = res->start; 222 223 switch (id) { 224 case PCI_DEVICE_ID_INTEL_SCH_LPC: 225 sch_gpio_core.base = 0; 226 sch_gpio_core.ngpio = 10; 227 sch_gpio_resume.base = 10; 228 sch_gpio_resume.ngpio = 4; 229 /* 230 * GPIO[6:0] enabled by default 231 * GPIO7 is configured by the CMC as SLPIOVR 232 * Enable GPIO[9:8] core powered gpios explicitly 233 */ 234 outb(0x3, gpio_ba + CGEN + 1); 235 /* 236 * SUS_GPIO[2:0] enabled by default 237 * Enable SUS_GPIO3 resume powered gpio explicitly 238 */ 239 outb(0x8, gpio_ba + RGEN); 240 break; 241 242 case PCI_DEVICE_ID_INTEL_ITC_LPC: 243 sch_gpio_core.base = 0; 244 sch_gpio_core.ngpio = 5; 245 sch_gpio_resume.base = 5; 246 sch_gpio_resume.ngpio = 9; 247 break; 248 249 case PCI_DEVICE_ID_INTEL_CENTERTON_ILB: 250 sch_gpio_core.base = 0; 251 sch_gpio_core.ngpio = 21; 252 sch_gpio_resume.base = 21; 253 sch_gpio_resume.ngpio = 9; 254 break; 255 256 default: 257 err = -ENODEV; 258 goto err_sch_gpio_core; 259 } 260 261 sch_gpio_core.dev = &pdev->dev; 262 sch_gpio_resume.dev = &pdev->dev; 263 264 err = gpiochip_add(&sch_gpio_core); 265 if (err < 0) 266 goto err_sch_gpio_core; 267 268 err = gpiochip_add(&sch_gpio_resume); 269 if (err < 0) 270 goto err_sch_gpio_resume; 271 272 return 0; 273 274 err_sch_gpio_resume: 275 if (gpiochip_remove(&sch_gpio_core)) 276 dev_err(&pdev->dev, "%s gpiochip_remove failed\n", __func__); 277 278 err_sch_gpio_core: 279 release_region(res->start, resource_size(res)); 280 gpio_ba = 0; 281 282 return err; 283 } 284 285 static int sch_gpio_remove(struct platform_device *pdev) 286 { 287 struct resource *res; 288 if (gpio_ba) { 289 int err; 290 291 err = gpiochip_remove(&sch_gpio_core); 292 if (err) 293 dev_err(&pdev->dev, "%s failed, %d\n", 294 "gpiochip_remove()", err); 295 err = gpiochip_remove(&sch_gpio_resume); 296 if (err) 297 dev_err(&pdev->dev, "%s failed, %d\n", 298 "gpiochip_remove()", err); 299 300 res = platform_get_resource(pdev, IORESOURCE_IO, 0); 301 302 release_region(res->start, resource_size(res)); 303 gpio_ba = 0; 304 305 return err; 306 } 307 308 return 0; 309 } 310 311 static struct platform_driver sch_gpio_driver = { 312 .driver = { 313 .name = "sch_gpio", 314 .owner = THIS_MODULE, 315 }, 316 .probe = sch_gpio_probe, 317 .remove = sch_gpio_remove, 318 }; 319 320 module_platform_driver(sch_gpio_driver); 321 322 MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>"); 323 MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH"); 324 MODULE_LICENSE("GPL"); 325 MODULE_ALIAS("platform:sch_gpio"); 326