ia64/linux-2.6.18-xen.hg

annotate drivers/char/pc8736x_gpio.c @ 893:f994bfe9b93b

linux/blktap2: reduce TLB flush scope

c/s 885 added very coarse TLB flushing. Since these flushes always
follow single page updates, single page flushes (when available) are
sufficient.

Signed-off-by: Jan Beulich <jbeulich@novell.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Jun 04 10:32:57 2009 +0100 (2009-06-04)
parents 831230e53067
children
rev   line source
ian@0 1 /* linux/drivers/char/pc8736x_gpio.c
ian@0 2
ian@0 3 National Semiconductor PC8736x GPIO driver. Allows a user space
ian@0 4 process to play with the GPIO pins.
ian@0 5
ian@0 6 Copyright (c) 2005,2006 Jim Cromie <jim.cromie@gmail.com>
ian@0 7
ian@0 8 adapted from linux/drivers/char/scx200_gpio.c
ian@0 9 Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>,
ian@0 10 */
ian@0 11
ian@0 12 #include <linux/fs.h>
ian@0 13 #include <linux/module.h>
ian@0 14 #include <linux/errno.h>
ian@0 15 #include <linux/kernel.h>
ian@0 16 #include <linux/init.h>
ian@0 17 #include <linux/cdev.h>
ian@0 18 #include <linux/io.h>
ian@0 19 #include <linux/ioport.h>
ian@0 20 #include <linux/mutex.h>
ian@0 21 #include <linux/nsc_gpio.h>
ian@0 22 #include <linux/platform_device.h>
ian@0 23 #include <asm/uaccess.h>
ian@0 24
ian@0 25 #define DEVNAME "pc8736x_gpio"
ian@0 26
ian@0 27 MODULE_AUTHOR("Jim Cromie <jim.cromie@gmail.com>");
ian@0 28 MODULE_DESCRIPTION("NatSemi/Winbond PC-8736x GPIO Pin Driver");
ian@0 29 MODULE_LICENSE("GPL");
ian@0 30
ian@0 31 static int major; /* default to dynamic major */
ian@0 32 module_param(major, int, 0);
ian@0 33 MODULE_PARM_DESC(major, "Major device number");
ian@0 34
ian@0 35 static DEFINE_MUTEX(pc8736x_gpio_config_lock);
ian@0 36 static unsigned pc8736x_gpio_base;
ian@0 37 static u8 pc8736x_gpio_shadow[4];
ian@0 38
ian@0 39 #define SIO_BASE1 0x2E /* 1st command-reg to check */
ian@0 40 #define SIO_BASE2 0x4E /* alt command-reg to check */
ian@0 41
ian@0 42 #define SIO_SID 0x20 /* SuperI/O ID Register */
ian@0 43 #define SIO_SID_VALUE 0xe9 /* Expected value in SuperI/O ID Register */
ian@0 44
ian@0 45 #define SIO_CF1 0x21 /* chip config, bit0 is chip enable */
ian@0 46
ian@0 47 #define PC8736X_GPIO_RANGE 16 /* ioaddr range */
ian@0 48 #define PC8736X_GPIO_CT 32 /* minors matching 4 8 bit ports */
ian@0 49
ian@0 50 #define SIO_UNIT_SEL 0x7 /* unit select reg */
ian@0 51 #define SIO_UNIT_ACT 0x30 /* unit enable */
ian@0 52 #define SIO_GPIO_UNIT 0x7 /* unit number of GPIO */
ian@0 53 #define SIO_VLM_UNIT 0x0D
ian@0 54 #define SIO_TMS_UNIT 0x0E
ian@0 55
ian@0 56 /* config-space addrs to read/write each unit's runtime addr */
ian@0 57 #define SIO_BASE_HADDR 0x60
ian@0 58 #define SIO_BASE_LADDR 0x61
ian@0 59
ian@0 60 /* GPIO config-space pin-control addresses */
ian@0 61 #define SIO_GPIO_PIN_SELECT 0xF0
ian@0 62 #define SIO_GPIO_PIN_CONFIG 0xF1
ian@0 63 #define SIO_GPIO_PIN_EVENT 0xF2
ian@0 64
ian@0 65 static unsigned char superio_cmd = 0;
ian@0 66 static unsigned char selected_device = 0xFF; /* bogus start val */
ian@0 67
ian@0 68 /* GPIO port runtime access, functionality */
ian@0 69 static int port_offset[] = { 0, 4, 8, 10 }; /* non-uniform offsets ! */
ian@0 70 /* static int event_capable[] = { 1, 1, 0, 0 }; ports 2,3 are hobbled */
ian@0 71
ian@0 72 #define PORT_OUT 0
ian@0 73 #define PORT_IN 1
ian@0 74 #define PORT_EVT_EN 2
ian@0 75 #define PORT_EVT_STST 3
ian@0 76
ian@0 77 static struct platform_device *pdev; /* use in dev_*() */
ian@0 78
ian@0 79 static inline void superio_outb(int addr, int val)
ian@0 80 {
ian@0 81 outb_p(addr, superio_cmd);
ian@0 82 outb_p(val, superio_cmd + 1);
ian@0 83 }
ian@0 84
ian@0 85 static inline int superio_inb(int addr)
ian@0 86 {
ian@0 87 outb_p(addr, superio_cmd);
ian@0 88 return inb_p(superio_cmd + 1);
ian@0 89 }
ian@0 90
ian@0 91 static int pc8736x_superio_present(void)
ian@0 92 {
ian@0 93 /* try the 2 possible values, read a hardware reg to verify */
ian@0 94 superio_cmd = SIO_BASE1;
ian@0 95 if (superio_inb(SIO_SID) == SIO_SID_VALUE)
ian@0 96 return superio_cmd;
ian@0 97
ian@0 98 superio_cmd = SIO_BASE2;
ian@0 99 if (superio_inb(SIO_SID) == SIO_SID_VALUE)
ian@0 100 return superio_cmd;
ian@0 101
ian@0 102 return 0;
ian@0 103 }
ian@0 104
ian@0 105 static void device_select(unsigned devldn)
ian@0 106 {
ian@0 107 superio_outb(SIO_UNIT_SEL, devldn);
ian@0 108 selected_device = devldn;
ian@0 109 }
ian@0 110
ian@0 111 static void select_pin(unsigned iminor)
ian@0 112 {
ian@0 113 /* select GPIO port/pin from device minor number */
ian@0 114 device_select(SIO_GPIO_UNIT);
ian@0 115 superio_outb(SIO_GPIO_PIN_SELECT,
ian@0 116 ((iminor << 1) & 0xF0) | (iminor & 0x7));
ian@0 117 }
ian@0 118
ian@0 119 static inline u32 pc8736x_gpio_configure_fn(unsigned index, u32 mask, u32 bits,
ian@0 120 u32 func_slct)
ian@0 121 {
ian@0 122 u32 config, new_config;
ian@0 123
ian@0 124 mutex_lock(&pc8736x_gpio_config_lock);
ian@0 125
ian@0 126 device_select(SIO_GPIO_UNIT);
ian@0 127 select_pin(index);
ian@0 128
ian@0 129 /* read current config value */
ian@0 130 config = superio_inb(func_slct);
ian@0 131
ian@0 132 /* set new config */
ian@0 133 new_config = (config & mask) | bits;
ian@0 134 superio_outb(func_slct, new_config);
ian@0 135
ian@0 136 mutex_unlock(&pc8736x_gpio_config_lock);
ian@0 137
ian@0 138 return config;
ian@0 139 }
ian@0 140
ian@0 141 static u32 pc8736x_gpio_configure(unsigned index, u32 mask, u32 bits)
ian@0 142 {
ian@0 143 return pc8736x_gpio_configure_fn(index, mask, bits,
ian@0 144 SIO_GPIO_PIN_CONFIG);
ian@0 145 }
ian@0 146
ian@0 147 static int pc8736x_gpio_get(unsigned minor)
ian@0 148 {
ian@0 149 int port, bit, val;
ian@0 150
ian@0 151 port = minor >> 3;
ian@0 152 bit = minor & 7;
ian@0 153 val = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_IN);
ian@0 154 val >>= bit;
ian@0 155 val &= 1;
ian@0 156
ian@0 157 dev_dbg(&pdev->dev, "_gpio_get(%d from %x bit %d) == val %d\n",
ian@0 158 minor, pc8736x_gpio_base + port_offset[port] + PORT_IN, bit,
ian@0 159 val);
ian@0 160
ian@0 161 return val;
ian@0 162 }
ian@0 163
ian@0 164 static void pc8736x_gpio_set(unsigned minor, int val)
ian@0 165 {
ian@0 166 int port, bit, curval;
ian@0 167
ian@0 168 minor &= 0x1f;
ian@0 169 port = minor >> 3;
ian@0 170 bit = minor & 7;
ian@0 171 curval = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_OUT);
ian@0 172
ian@0 173 dev_dbg(&pdev->dev, "addr:%x cur:%x bit-pos:%d cur-bit:%x + new:%d -> bit-new:%d\n",
ian@0 174 pc8736x_gpio_base + port_offset[port] + PORT_OUT,
ian@0 175 curval, bit, (curval & ~(1 << bit)), val, (val << bit));
ian@0 176
ian@0 177 val = (curval & ~(1 << bit)) | (val << bit);
ian@0 178
ian@0 179 dev_dbg(&pdev->dev, "gpio_set(minor:%d port:%d bit:%d)"
ian@0 180 " %2x -> %2x\n", minor, port, bit, curval, val);
ian@0 181
ian@0 182 outb_p(val, pc8736x_gpio_base + port_offset[port] + PORT_OUT);
ian@0 183
ian@0 184 curval = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_OUT);
ian@0 185 val = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_IN);
ian@0 186
ian@0 187 dev_dbg(&pdev->dev, "wrote %x, read: %x\n", curval, val);
ian@0 188 pc8736x_gpio_shadow[port] = val;
ian@0 189 }
ian@0 190
ian@0 191 static void pc8736x_gpio_set_high(unsigned index)
ian@0 192 {
ian@0 193 pc8736x_gpio_set(index, 1);
ian@0 194 }
ian@0 195
ian@0 196 static void pc8736x_gpio_set_low(unsigned index)
ian@0 197 {
ian@0 198 pc8736x_gpio_set(index, 0);
ian@0 199 }
ian@0 200
ian@0 201 static int pc8736x_gpio_current(unsigned minor)
ian@0 202 {
ian@0 203 int port, bit;
ian@0 204 minor &= 0x1f;
ian@0 205 port = minor >> 3;
ian@0 206 bit = minor & 7;
ian@0 207 return ((pc8736x_gpio_shadow[port] >> bit) & 0x01);
ian@0 208 }
ian@0 209
ian@0 210 static void pc8736x_gpio_change(unsigned index)
ian@0 211 {
ian@0 212 pc8736x_gpio_set(index, !pc8736x_gpio_current(index));
ian@0 213 }
ian@0 214
ian@0 215 static struct nsc_gpio_ops pc8736x_gpio_ops = {
ian@0 216 .owner = THIS_MODULE,
ian@0 217 .gpio_config = pc8736x_gpio_configure,
ian@0 218 .gpio_dump = nsc_gpio_dump,
ian@0 219 .gpio_get = pc8736x_gpio_get,
ian@0 220 .gpio_set = pc8736x_gpio_set,
ian@0 221 .gpio_change = pc8736x_gpio_change,
ian@0 222 .gpio_current = pc8736x_gpio_current
ian@0 223 };
ian@0 224
ian@0 225 static int pc8736x_gpio_open(struct inode *inode, struct file *file)
ian@0 226 {
ian@0 227 unsigned m = iminor(inode);
ian@0 228 file->private_data = &pc8736x_gpio_ops;
ian@0 229
ian@0 230 dev_dbg(&pdev->dev, "open %d\n", m);
ian@0 231
ian@0 232 if (m >= PC8736X_GPIO_CT)
ian@0 233 return -EINVAL;
ian@0 234 return nonseekable_open(inode, file);
ian@0 235 }
ian@0 236
ian@0 237 static const struct file_operations pc8736x_gpio_fileops = {
ian@0 238 .owner = THIS_MODULE,
ian@0 239 .open = pc8736x_gpio_open,
ian@0 240 .write = nsc_gpio_write,
ian@0 241 .read = nsc_gpio_read,
ian@0 242 };
ian@0 243
ian@0 244 static void __init pc8736x_init_shadow(void)
ian@0 245 {
ian@0 246 int port;
ian@0 247
ian@0 248 /* read the current values driven on the GPIO signals */
ian@0 249 for (port = 0; port < 4; ++port)
ian@0 250 pc8736x_gpio_shadow[port]
ian@0 251 = inb_p(pc8736x_gpio_base + port_offset[port]
ian@0 252 + PORT_OUT);
ian@0 253
ian@0 254 }
ian@0 255
ian@0 256 static struct cdev pc8736x_gpio_cdev;
ian@0 257
ian@0 258 static int __init pc8736x_gpio_init(void)
ian@0 259 {
ian@0 260 int rc;
ian@0 261 dev_t devid;
ian@0 262
ian@0 263 pdev = platform_device_alloc(DEVNAME, 0);
ian@0 264 if (!pdev)
ian@0 265 return -ENOMEM;
ian@0 266
ian@0 267 rc = platform_device_add(pdev);
ian@0 268 if (rc) {
ian@0 269 rc = -ENODEV;
ian@0 270 goto undo_platform_dev_alloc;
ian@0 271 }
ian@0 272 dev_info(&pdev->dev, "NatSemi pc8736x GPIO Driver Initializing\n");
ian@0 273
ian@0 274 if (!pc8736x_superio_present()) {
ian@0 275 rc = -ENODEV;
ian@0 276 dev_err(&pdev->dev, "no device found\n");
ian@0 277 goto undo_platform_dev_add;
ian@0 278 }
ian@0 279 pc8736x_gpio_ops.dev = &pdev->dev;
ian@0 280
ian@0 281 /* Verify that chip and it's GPIO unit are both enabled.
ian@0 282 My BIOS does this, so I take minimum action here
ian@0 283 */
ian@0 284 rc = superio_inb(SIO_CF1);
ian@0 285 if (!(rc & 0x01)) {
ian@0 286 rc = -ENODEV;
ian@0 287 dev_err(&pdev->dev, "device not enabled\n");
ian@0 288 goto undo_platform_dev_add;
ian@0 289 }
ian@0 290 device_select(SIO_GPIO_UNIT);
ian@0 291 if (!superio_inb(SIO_UNIT_ACT)) {
ian@0 292 rc = -ENODEV;
ian@0 293 dev_err(&pdev->dev, "GPIO unit not enabled\n");
ian@0 294 goto undo_platform_dev_add;
ian@0 295 }
ian@0 296
ian@0 297 /* read the GPIO unit base addr that chip responds to */
ian@0 298 pc8736x_gpio_base = (superio_inb(SIO_BASE_HADDR) << 8
ian@0 299 | superio_inb(SIO_BASE_LADDR));
ian@0 300
ian@0 301 if (!request_region(pc8736x_gpio_base, PC8736X_GPIO_RANGE, DEVNAME)) {
ian@0 302 rc = -ENODEV;
ian@0 303 dev_err(&pdev->dev, "GPIO ioport %x busy\n",
ian@0 304 pc8736x_gpio_base);
ian@0 305 goto undo_platform_dev_add;
ian@0 306 }
ian@0 307 dev_info(&pdev->dev, "GPIO ioport %x reserved\n", pc8736x_gpio_base);
ian@0 308
ian@0 309 if (major) {
ian@0 310 devid = MKDEV(major, 0);
ian@0 311 rc = register_chrdev_region(devid, PC8736X_GPIO_CT, DEVNAME);
ian@0 312 } else {
ian@0 313 rc = alloc_chrdev_region(&devid, 0, PC8736X_GPIO_CT, DEVNAME);
ian@0 314 major = MAJOR(devid);
ian@0 315 }
ian@0 316
ian@0 317 if (rc < 0) {
ian@0 318 dev_err(&pdev->dev, "register-chrdev failed: %d\n", rc);
ian@0 319 goto undo_request_region;
ian@0 320 }
ian@0 321 if (!major) {
ian@0 322 major = rc;
ian@0 323 dev_dbg(&pdev->dev, "got dynamic major %d\n", major);
ian@0 324 }
ian@0 325
ian@0 326 pc8736x_init_shadow();
ian@0 327
ian@0 328 /* ignore minor errs, and succeed */
ian@0 329 cdev_init(&pc8736x_gpio_cdev, &pc8736x_gpio_fileops);
ian@0 330 cdev_add(&pc8736x_gpio_cdev, devid, PC8736X_GPIO_CT);
ian@0 331
ian@0 332 return 0;
ian@0 333
ian@0 334 undo_request_region:
ian@0 335 release_region(pc8736x_gpio_base, PC8736X_GPIO_RANGE);
ian@0 336 undo_platform_dev_add:
ian@0 337 platform_device_del(pdev);
ian@0 338 undo_platform_dev_alloc:
ian@0 339 platform_device_put(pdev);
ian@0 340
ian@0 341 return rc;
ian@0 342 }
ian@0 343
ian@0 344 static void __exit pc8736x_gpio_cleanup(void)
ian@0 345 {
ian@0 346 dev_dbg(&pdev->dev, "cleanup\n");
ian@0 347
ian@0 348 cdev_del(&pc8736x_gpio_cdev);
ian@0 349 unregister_chrdev_region(MKDEV(major,0), PC8736X_GPIO_CT);
ian@0 350 release_region(pc8736x_gpio_base, PC8736X_GPIO_RANGE);
ian@0 351
ian@0 352 platform_device_del(pdev);
ian@0 353 platform_device_put(pdev);
ian@0 354 }
ian@0 355
ian@0 356 module_init(pc8736x_gpio_init);
ian@0 357 module_exit(pc8736x_gpio_cleanup);