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