1/* 2 * sch_gpio.c - 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 29#include <linux/gpio.h> 30 31static DEFINE_SPINLOCK(gpio_lock); 32 33#define CGEN (0x00) 34#define CGIO (0x04) 35#define CGLV (0x08) 36 37#define RGEN (0x20) 38#define RGIO (0x24) 39#define RGLV (0x28) 40 41static unsigned short gpio_ba; 42 43static int sch_gpio_core_direction_in(struct gpio_chip *gc, unsigned gpio_num) 44{ 45 u8 curr_dirs; 46 unsigned short offset, bit; 47 48 spin_lock(&gpio_lock); 49 50 offset = CGIO + gpio_num / 8; 51 bit = gpio_num % 8; 52 53 curr_dirs = inb(gpio_ba + offset); 54 55 if (!(curr_dirs & (1 << bit))) 56 outb(curr_dirs | (1 << bit), gpio_ba + offset); 57 58 spin_unlock(&gpio_lock); 59 return 0; 60} 61 62static int sch_gpio_core_get(struct gpio_chip *gc, unsigned gpio_num) 63{ 64 int res; 65 unsigned short offset, bit; 66 67 offset = CGLV + gpio_num / 8; 68 bit = gpio_num % 8; 69 70 res = !!(inb(gpio_ba + offset) & (1 << bit)); 71 return res; 72} 73 74static void sch_gpio_core_set(struct gpio_chip *gc, unsigned gpio_num, int val) 75{ 76 u8 curr_vals; 77 unsigned short offset, bit; 78 79 spin_lock(&gpio_lock); 80 81 offset = CGLV + gpio_num / 8; 82 bit = gpio_num % 8; 83 84 curr_vals = inb(gpio_ba + offset); 85 86 if (val) 87 outb(curr_vals | (1 << bit), gpio_ba + offset); 88 else 89 outb((curr_vals & ~(1 << bit)), gpio_ba + offset); 90 spin_unlock(&gpio_lock); 91} 92 93static int sch_gpio_core_direction_out(struct gpio_chip *gc, 94 unsigned gpio_num, int val) 95{ 96 u8 curr_dirs; 97 unsigned short offset, bit; 98 99 sch_gpio_core_set(gc, gpio_num, val); 100 101 spin_lock(&gpio_lock); 102 103 offset = CGIO + gpio_num / 8; 104 bit = gpio_num % 8; 105 106 curr_dirs = inb(gpio_ba + offset); 107 if (curr_dirs & (1 << bit)) 108 outb(curr_dirs & ~(1 << bit), gpio_ba + offset); 109 110 spin_unlock(&gpio_lock); 111 return 0; 112} 113 114static struct gpio_chip sch_gpio_core = { 115 .label = "sch_gpio_core", 116 .owner = THIS_MODULE, 117 .direction_input = sch_gpio_core_direction_in, 118 .get = sch_gpio_core_get, 119 .direction_output = sch_gpio_core_direction_out, 120 .set = sch_gpio_core_set, 121}; 122 123static int sch_gpio_resume_direction_in(struct gpio_chip *gc, 124 unsigned gpio_num) 125{ 126 u8 curr_dirs; 127 128 spin_lock(&gpio_lock); 129 130 curr_dirs = inb(gpio_ba + RGIO); 131 132 if (!(curr_dirs & (1 << gpio_num))) 133 outb(curr_dirs | (1 << gpio_num) , gpio_ba + RGIO); 134 135 spin_unlock(&gpio_lock); 136 return 0; 137} 138 139static int sch_gpio_resume_get(struct gpio_chip *gc, unsigned gpio_num) 140{ 141 return !!(inb(gpio_ba + RGLV) & (1 << gpio_num)); 142} 143 144static void sch_gpio_resume_set(struct gpio_chip *gc, 145 unsigned gpio_num, int val) 146{ 147 u8 curr_vals; 148 149 spin_lock(&gpio_lock); 150 151 curr_vals = inb(gpio_ba + RGLV); 152 153 if (val) 154 outb(curr_vals | (1 << gpio_num), gpio_ba + RGLV); 155 else 156 outb((curr_vals & ~(1 << gpio_num)), gpio_ba + RGLV); 157 158 spin_unlock(&gpio_lock); 159} 160 161static int sch_gpio_resume_direction_out(struct gpio_chip *gc, 162 unsigned gpio_num, int val) 163{ 164 u8 curr_dirs; 165 166 sch_gpio_resume_set(gc, gpio_num, val); 167 168 spin_lock(&gpio_lock); 169 170 curr_dirs = inb(gpio_ba + RGIO); 171 if (curr_dirs & (1 << gpio_num)) 172 outb(curr_dirs & ~(1 << gpio_num), gpio_ba + RGIO); 173 174 spin_unlock(&gpio_lock); 175 return 0; 176} 177 178static struct gpio_chip sch_gpio_resume = { 179 .label = "sch_gpio_resume", 180 .owner = THIS_MODULE, 181 .direction_input = sch_gpio_resume_direction_in, 182 .get = sch_gpio_resume_get, 183 .direction_output = sch_gpio_resume_direction_out, 184 .set = sch_gpio_resume_set, 185}; 186 187static int __devinit sch_gpio_probe(struct platform_device *pdev) 188{ 189 struct resource *res; 190 int err; 191 192 res = platform_get_resource(pdev, IORESOURCE_IO, 0); 193 if (!res) 194 return -EBUSY; 195 196 if (!request_region(res->start, resource_size(res), pdev->name)) 197 return -EBUSY; 198 199 gpio_ba = res->start; 200 201 sch_gpio_core.base = 0; 202 sch_gpio_core.ngpio = 10; 203 sch_gpio_core.dev = &pdev->dev; 204 205 sch_gpio_resume.base = 10; 206 sch_gpio_resume.ngpio = 4; 207 sch_gpio_resume.dev = &pdev->dev; 208 209 err = gpiochip_add(&sch_gpio_core); 210 if (err < 0) 211 goto err_sch_gpio_core; 212 213 err = gpiochip_add(&sch_gpio_resume); 214 if (err < 0) 215 goto err_sch_gpio_resume; 216 217 /* 218 * GPIO[6:0] enabled by default 219 * GPIO7 is configured by the CMC as SLPIOVR 220 * Enable GPIO[9:8] core powered gpios explicitly 221 */ 222 outb(0x3, gpio_ba + CGEN + 1); 223 /* 224 * SUS_GPIO[2:0] enabled by default 225 * Enable SUS_GPIO3 resume powered gpio explicitly 226 */ 227 outb(0x8, gpio_ba + RGEN); 228 229 return 0; 230 231err_sch_gpio_resume: 232 err = gpiochip_remove(&sch_gpio_core); 233 if (err) 234 dev_err(&pdev->dev, "%s failed, %d\n", 235 "gpiochip_remove()", err); 236 237err_sch_gpio_core: 238 release_region(res->start, resource_size(res)); 239 gpio_ba = 0; 240 241 return err; 242} 243 244static int __devexit sch_gpio_remove(struct platform_device *pdev) 245{ 246 struct resource *res; 247 if (gpio_ba) { 248 int err; 249 250 err = gpiochip_remove(&sch_gpio_core); 251 if (err) 252 dev_err(&pdev->dev, "%s failed, %d\n", 253 "gpiochip_remove()", err); 254 err = gpiochip_remove(&sch_gpio_resume); 255 if (err) 256 dev_err(&pdev->dev, "%s failed, %d\n", 257 "gpiochip_remove()", err); 258 259 res = platform_get_resource(pdev, IORESOURCE_IO, 0); 260 261 release_region(res->start, resource_size(res)); 262 gpio_ba = 0; 263 264 return err; 265 } 266 267 return 0; 268} 269 270static struct platform_driver sch_gpio_driver = { 271 .driver = { 272 .name = "sch_gpio", 273 .owner = THIS_MODULE, 274 }, 275 .probe = sch_gpio_probe, 276 .remove = __devexit_p(sch_gpio_remove), 277}; 278 279static int __init sch_gpio_init(void) 280{ 281 return platform_driver_register(&sch_gpio_driver); 282} 283 284static void __exit sch_gpio_exit(void) 285{ 286 platform_driver_unregister(&sch_gpio_driver); 287} 288 289module_init(sch_gpio_init); 290module_exit(sch_gpio_exit); 291 292MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>"); 293MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH"); 294MODULE_LICENSE("GPL"); 295MODULE_ALIAS("platform:sch_gpio"); 296