/* * drivers/user-pins.c * * Copyright (C) 2008 Palm, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_PINMUX #include #endif #include #include #include #include #undef MODDEBUG //#define MODDEBUG 1 #ifdef MODDEBUG #define PDBG(args...) printk(args) #else #define PDBG(args...) #endif #define DRIVER_NAME "user-pins" enum event { USER_PIN_EVENT_IRQ, USER_PIN_EVENT_IRQ_READ, }; struct user_pins_log_event { ktime_t timestamp; enum event event; int gpio; }; #if defined(CONFIG_DEBUG_FS) #define NR_LOG_ENTRIES 512 static struct user_pins_log_event user_pins_log[NR_LOG_ENTRIES]; static int user_pins_log_idx; static DEFINE_SPINLOCK(debug_lock); static char debug_buffer[PAGE_SIZE]; static void user_pins_log_event(int gpio, enum event event) { unsigned long flags; spin_lock_irqsave(&debug_lock, flags); user_pins_log[user_pins_log_idx].timestamp = ktime_get(); user_pins_log[user_pins_log_idx].event = event; user_pins_log[user_pins_log_idx].gpio = gpio; user_pins_log_idx += 1; if (user_pins_log_idx == NR_LOG_ENTRIES) { user_pins_log_idx = 0; } spin_unlock_irqrestore(&debug_lock, flags); } static int debug_open(struct inode *inode, struct file *file) { file->private_data = inode->i_private; return 0; } static ssize_t debug_show_log(struct file *file, char __user *ubuf, size_t count, loff_t *ppos) { char *buf = debug_buffer; int i, n = 0; const char *event; unsigned long flags; spin_lock_irqsave(&debug_lock, flags); for (i = 0; i < user_pins_log_idx; i++) { switch (user_pins_log[i].event) { case USER_PIN_EVENT_IRQ: event = "IRQ"; break; case USER_PIN_EVENT_IRQ_READ: event = "IRQ_READ"; break; default: event = ""; break; } n += scnprintf(buf + n, PAGE_SIZE - n, "%010llu: %-8d %s\n", ktime_to_ns(user_pins_log[i].timestamp), user_pins_log[i].gpio, event); } user_pins_log_idx = 0; spin_unlock_irqrestore(&debug_lock, flags); return simple_read_from_buffer(ubuf, count, ppos, buf, n); } static const struct file_operations debug_log_fops = { .open = debug_open, .read = debug_show_log, }; static void user_pins_debug_init(void) { struct dentry *dent; dent = debugfs_create_dir(DRIVER_NAME, 0); if (IS_ERR(dent)) { return; } debugfs_create_file("log", 0444, dent, NULL, &debug_log_fops); user_pins_log_idx = 0; } #else static void user_pins_log_event(int gpio, enum event event) { } static void user_pins_debug_init(void) { } #endif static struct kobject *user_hw_kobj; static struct kobject *pins_kobj; DEFINE_SPINLOCK(pins_lock); static int __init user_hw_init(void) { user_hw_kobj = kobject_create_and_add("user_hw", NULL); if (user_hw_kobj == NULL) { return -ENOMEM; } return 0; } arch_initcall(user_hw_init); struct pin_attribute { struct attribute attr; ssize_t (*show) (struct pin_attribute *attr, char *buf); ssize_t (*store)(struct pin_attribute *attr, const char *buf, size_t count); }; #define to_pin_attr(_attr) container_of(_attr, struct pin_attribute, attr) static ssize_t pin_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct pin_attribute * pin_attr = to_pin_attr(attr); if (pin_attr->show) { return pin_attr->show(pin_attr, buf); } else { return -EIO; } return 0; } static ssize_t pin_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { struct pin_attribute * pin_attr = to_pin_attr(attr); if (pin_attr->store) { return pin_attr->store (pin_attr, buf, count); } else { return -EIO; } } static struct sysfs_ops pin_sysfs_ops = { .show = pin_attr_show, .store = pin_attr_store, }; static struct kobj_type ktype_pin = { .release = NULL, .sysfs_ops = &pin_sysfs_ops, }; struct gpio_pin { int gpio; int options; int direction; int act_level; int def_level; int active_power_collapse; irqreturn_t (*irq_handler)(int irq, void *data); int (*pinmux)(int gpio, int mode); atomic_t irq_count; int irq_config; int irq_masked; int irq_requested; const char * name; int irq_handle_mode; atomic_t irqs_during_suspend; struct sysfs_dirent *sd; struct pin_attribute attr_gpio; struct pin_attribute attr_level; struct pin_attribute attr_active; struct pin_attribute attr_direction; struct pin_attribute attr_irq; struct pin_attribute attr_irqconfig; struct pin_attribute attr_irqrequest; struct pin_attribute attr_irqmask; struct pin_attribute attr_irq_handle_mode; struct pin_attribute attr_active_power_collapse; struct attribute *attr_ptr_arr[11]; }; struct gpio_pin_set_item { struct attribute_group attr_grp; struct gpio_pin pin; }; struct gpio_pin_set { const char *set_name; struct kobject kobj; int num_pins; struct gpio_pin_set_item pins[]; }; struct gpio_pin_dev_ctxt { int num_sets; struct gpio_pin_set *sets[]; }; /* * Show irq handle mode * * If AUTO, irq will be handled by irq_handler */ static int pin_show_irq_mode ( struct pin_attribute *attr, char *buf) { struct gpio_pin *pin = container_of(attr, struct gpio_pin, attr_irq_handle_mode ); return sprintf(buf, "%d\n", pin->irq_handle_mode ); } /* * Set irq handle mode for specified pin * */ static ssize_t pin_store_irq_mode( struct pin_attribute *attr, const char * buf, size_t count) { int irq_handle_mode; unsigned long flags; struct gpio_pin *pin = container_of(attr, struct gpio_pin, attr_irq_handle_mode ); sscanf(buf, "%d", &irq_handle_mode); spin_lock_irqsave(&pins_lock, flags); // reset irq count to detect user suspend and kernel suspend pin->irq_handle_mode = irq_handle_mode; if(pin->irq_handle_mode == IRQ_HANDLE_OFF) atomic_set(&pin->irqs_during_suspend, 0); spin_unlock_irqrestore(&pins_lock, flags); printk(KERN_INFO"USERPIN: setting irq handle mode of pin gpio %d to %d\n", pin->gpio, irq_handle_mode); return count; } static irqreturn_t user_pins_irq(int irq, void *data) { unsigned long flags; struct gpio_pin *pin = (struct gpio_pin *)data; user_pins_log_event(pin->gpio, USER_PIN_EVENT_IRQ); atomic_inc(&pin->irq_count); spin_lock_irqsave(&pins_lock, flags); if (pin->irq_handle_mode & IRQ_HANDLE_OFF) atomic_inc(&pin->irqs_during_suspend); spin_unlock_irqrestore(&pins_lock, flags); if (pin->sd != NULL) { sysfs_notify_dirent(pin->sd); } if (pin->irq_handler != NULL) { pin->irq_handler(irq, NULL); } return IRQ_HANDLED; } static int user_pins_irq_request(struct gpio_pin *pin) { int rc = 0; printk("user-pins: configuring irq for gpio %d\n", pin->gpio); rc = request_irq(gpio_to_irq(pin->gpio), user_pins_irq, pin->irq_config, "userpins", pin); if (rc) { printk("user-pins: failed to request irq\n"); } return rc; } /* * Show gpio direction */ static int pin_show_direction(struct pin_attribute *attr, char *buf) { struct gpio_pin *pin = container_of(attr, struct gpio_pin, attr_direction); return sprintf(buf, "%d\n", pin->direction); } /* * Show active level for specified pin */ static int pin_show_active(struct pin_attribute *attr, char *buf) { struct gpio_pin *pin = container_of(attr, struct gpio_pin, attr_active); return sprintf(buf, "%d\n", pin->act_level); } static int pin_show_active_power_collapse(struct pin_attribute *attr, char *buf) { struct gpio_pin *pin = container_of(attr, struct gpio_pin, attr_active_power_collapse); return sprintf(buf, "%d\n", pin->active_power_collapse); } /* * Show gpio number */ static int pin_show_gpio(struct pin_attribute *attr, char *buf) { struct gpio_pin *pin = container_of(attr, struct gpio_pin, attr_gpio); return sprintf(buf, "%d\n", pin->gpio); } /* * Show current for specified pin */ static int pin_show_level(struct pin_attribute *attr, char *buf) { int val; struct gpio_pin *pin = container_of(attr, struct gpio_pin, attr_level); val = gpio_get_value(pin->gpio); PDBG("get: gpio[%d] = %d\n", pin->gpio, val); if (val) { return sprintf(buf, "1\n" ); } else { return sprintf(buf, "0\n" ); } } /* * Set level for specified pin */ static ssize_t pin_store_level(struct pin_attribute *attr, const char *buf, size_t count) { int i = 0, len, val = -1; struct gpio_pin *pin = container_of(attr, struct gpio_pin, attr_level); if (pin->options & PIN_READ_ONLY) { return count; // just ignore writes } /* skip leading white spaces */ while (i < count && isspace(buf[i])) { i++; } len = count - i; if (len >= 1 && strncmp(buf+i, "1", 1) == 0) { val = 1; goto set; } if (len >= 1 && strncmp(buf+i, "0", 1) == 0) { val = 0; goto set; } if (len >= 4 && strncmp(buf+i, "high", 4) == 0) { val = 1; goto set; } if (len >= 3 && strncmp(buf+i, "low", 3) == 0) { val = 0; goto set; } return count; set: PDBG("set: gpio[%d] = %d\n", pin->gpio, val); gpio_set_value(pin->gpio, val); return count; } static ssize_t pin_store_active_power_collapse(struct pin_attribute *attr, const char *buf, size_t count) { int i = 0, len, val = -1; struct gpio_pin *pin = container_of(attr, struct gpio_pin, attr_active_power_collapse); if (pin->options & PIN_READ_ONLY) { return count; // just ignore writes } /* skip leading white spaces */ while (i < count && isspace(buf[i])) { i++; } len = count - i; if (len >= 1 && strncmp(buf+i, "1", 1) == 0) { val = 1; } else if (len >= 1 && strncmp(buf+i, "0", 1) == 0) { val = 0; } else { /* invalid input */ goto end; } PDBG("set: active_power_collapse[%d] = %d\n", pin->gpio, val); #ifdef CONFIG_PINMUX // This is an old legacy mechanism for muxing pins, it's not that clean // and creates dependency on pinmux driver that is not really // used on some systems. pinmux_set_power_collapse(pin->name, val); #endif pin->active_power_collapse = val; end: return count; } static int pin_show_irq(struct pin_attribute *attr, char *buf) { struct gpio_pin *pin = container_of(attr, struct gpio_pin, attr_irq); int count = atomic_xchg(&pin->irq_count, 0); user_pins_log_event(pin->gpio, USER_PIN_EVENT_IRQ_READ); return sprintf(buf, "%d\n", count); } static int pin_show_irqconfig(struct pin_attribute *attr, char *buf) { struct gpio_pin *pin = container_of(attr, struct gpio_pin, attr_irqconfig); return sprintf(buf, "%d\n", pin->irq_config); } static ssize_t pin_store_irqconfig(struct pin_attribute *attr, const char *buf, size_t count) { struct gpio_pin *pin = container_of(attr, struct gpio_pin, attr_irqconfig); unsigned long flags; int config; config = simple_strtoul(buf, NULL, 10) & IRQF_TRIGGER_MASK; spin_lock_irqsave(&pins_lock, flags); pin->irq_config = config; spin_unlock_irqrestore(&pins_lock, flags); return count; } static int pin_show_irqrequest(struct pin_attribute *attr, char *buf) { struct gpio_pin *pin = container_of(attr, struct gpio_pin, attr_irqrequest); return sprintf(buf, "%d\n", !!pin->irq_requested); } static ssize_t pin_store_irqrequest(struct pin_attribute *attr, const char *buf, size_t count) { struct gpio_pin *pin = container_of(attr, struct gpio_pin, attr_irqrequest); unsigned long flags; int request; int rc = 0; if ((count > 0) && (buf[0] == '1')) { request = 1; } else { request = 0; } if (request != pin->irq_requested) { if (request) { rc = user_pins_irq_request(pin); if (rc) { goto fail; } } else { free_irq(gpio_to_irq(pin->gpio), pin); } spin_lock_irqsave(&pins_lock, flags); pin->irq_requested = request; pin->irq_masked = 0; spin_unlock_irqrestore(&pins_lock, flags); } return count; fail: return rc; } static int pin_show_irqmask(struct pin_attribute *attr, char *buf) { struct gpio_pin *pin = container_of(attr, struct gpio_pin, attr_irqmask); return sprintf(buf, "%d\n", !!pin->irq_masked); } static ssize_t pin_store_irqmask(struct pin_attribute *attr, const char *buf, size_t count) { struct gpio_pin *pin = container_of(attr, struct gpio_pin, attr_irqmask); unsigned long flags; int mask; if ((count > 0) && (buf[0] == '1')) { mask = 1; } else { mask = 0; } spin_lock_irqsave(&pins_lock, flags); if (mask != pin->irq_masked) { if (mask) { disable_irq(gpio_to_irq(pin->gpio)); } else { enable_irq(gpio_to_irq(pin->gpio)); } pin->irq_masked = mask; } spin_unlock_irqrestore(&pins_lock, flags); return count; } static void pin_set_item_init(struct gpio_pin_set_item *psi, struct user_pin *up) { psi->pin.name = up->name; psi->pin.gpio = up->gpio; psi->pin.options = up->options; psi->pin.direction = up->direction; psi->pin.act_level = up->act_level; psi->pin.def_level = up->def_level; psi->pin.irq_handler = up->irq_handler; psi->pin.pinmux = up->pinmux; psi->pin.irq_config = up->irq_config; psi->pin.irq_handle_mode = up->irq_handle_mode; atomic_set(&psi->pin.irqs_during_suspend, 0); atomic_set(&psi->pin.irq_count, 0); psi->pin.irq_requested = 0; psi->pin.irq_masked = 0; // gpio attr psi->pin.attr_gpio.attr.name = "gpio"; psi->pin.attr_gpio.attr.mode = 0444; psi->pin.attr_gpio.show = pin_show_gpio; // level attr psi->pin.attr_level.attr.name = "level"; psi->pin.attr_level.attr.mode = 0644; psi->pin.attr_level.show = pin_show_level; psi->pin.attr_level.store = pin_store_level; // active attr psi->pin.attr_active.attr.name = "active"; psi->pin.attr_active.attr.mode = 0444; psi->pin.attr_active.show = pin_show_active; // direction psi->pin.attr_direction.attr.name = "direction"; psi->pin.attr_direction.attr.mode = 0444; psi->pin.attr_direction.show = pin_show_direction; psi->pin.attr_active_power_collapse.attr.name = "active_power_collapse"; psi->pin.attr_active_power_collapse.attr.mode = 0644; psi->pin.attr_active_power_collapse.show = pin_show_active_power_collapse; psi->pin.attr_active_power_collapse.store = pin_store_active_power_collapse; if (psi->pin.options & PIN_IRQ) { // irq psi->pin.attr_irq.attr.name = "irq"; psi->pin.attr_irq.attr.mode = 0444; psi->pin.attr_irq.show = pin_show_irq; // irqconfig psi->pin.attr_irqconfig.attr.name = "irqconfig"; psi->pin.attr_irqconfig.attr.mode = 0666; psi->pin.attr_irqconfig.show = pin_show_irqconfig; psi->pin.attr_irqconfig.store = pin_store_irqconfig; // irqrequest psi->pin.attr_irqrequest.attr.name = "irqrequest"; psi->pin.attr_irqrequest.attr.mode = 0666; psi->pin.attr_irqrequest.show = pin_show_irqrequest; psi->pin.attr_irqrequest.store = pin_store_irqrequest; // irqmask psi->pin.attr_irqmask.attr.name = "irqmask"; psi->pin.attr_irqmask.attr.mode = 0666; psi->pin.attr_irqmask.show = pin_show_irqmask; psi->pin.attr_irqmask.store = pin_store_irqmask; // irq handle mode psi->pin.attr_irq_handle_mode.attr.name = "irq_handle_mode"; psi->pin.attr_irq_handle_mode.attr.mode = 0644; psi->pin.attr_irq_handle_mode.show = pin_show_irq_mode; psi->pin.attr_irq_handle_mode.store = pin_store_irq_mode; } // setup attr pointer array { int i = 0; psi->pin.attr_ptr_arr[i++] = &psi->pin.attr_gpio.attr; psi->pin.attr_ptr_arr[i++] = &psi->pin.attr_level.attr; psi->pin.attr_ptr_arr[i++] = &psi->pin.attr_active.attr; psi->pin.attr_ptr_arr[i++] = &psi->pin.attr_direction.attr; psi->pin.attr_ptr_arr[i++] = &psi->pin.attr_active_power_collapse.attr; if (psi->pin.options & PIN_IRQ) { psi->pin.attr_ptr_arr[i++] = &psi->pin.attr_irq.attr; psi->pin.attr_ptr_arr[i++] = &psi->pin.attr_irqconfig.attr; psi->pin.attr_ptr_arr[i++] = &psi->pin.attr_irqrequest.attr; psi->pin.attr_ptr_arr[i++] = &psi->pin.attr_irqmask.attr; psi->pin.attr_ptr_arr[i++] = &psi->pin.attr_irq_handle_mode.attr; } psi->pin.attr_ptr_arr[i++] = NULL; /* if this is triggered, then we need a larger attr_ptr_arr array */ BUG_ON(i > ARRAY_SIZE(psi->pin.attr_ptr_arr)); } // setup attribute group psi->attr_grp.name = psi->pin.name; psi->attr_grp.attrs = psi->pin.attr_ptr_arr; return; } /* * */ static struct gpio_pin_set * pin_set_alloc(struct user_pin_set *ups) { int i; struct gpio_pin_set *gps = NULL; gps = kzalloc(sizeof(struct gpio_pin_set) + ups->num_pins * sizeof(struct gpio_pin_set_item), GFP_KERNEL); if (gps == NULL) { return NULL; } gps->num_pins = ups->num_pins; gps->set_name = ups->set_name; for (i = 0; i < gps->num_pins; i++) { pin_set_item_init(&gps->pins[i], &ups->pins[i]); } return gps; } /* * Registers specified pin set */ static int pin_set_register(struct gpio_pin_set *s) { int rc, i; struct sysfs_dirent *grp_sd; if (s == NULL) { return -EINVAL; } rc = kobject_init_and_add(&s->kobj, &ktype_pin, pins_kobj, s->set_name); if (rc) { printk (KERN_ERR "Failed to register kobject (%s)\n", s->set_name); return -ENODEV; } /* for all pins */ for (i = 0; i < s->num_pins; i++) { rc = gpio_request(s->pins[i].pin.gpio, "gpio"); if (rc) { printk(KERN_ERR "Failed to request gpio (%d)\n", s->pins[i].pin.gpio); continue; } if (s->pins[i].pin.direction != -1) { // direction is set if (s->pins[i].pin.direction == 0) { // an output /* A setting of def_level == -1 means that we * keep the current level of the GPIO. * Otherwise we set def_level. */ int level = (-1 == s->pins[i].pin.def_level) ? gpio_get_value(s->pins[i].pin.gpio) : s->pins[i].pin.def_level; gpio_direction_output(s->pins[i].pin.gpio, level); } else { // an input gpio_direction_input(s->pins[i].pin.gpio); } } // create attribute group rc = sysfs_create_group(&s->kobj, &s->pins[i].attr_grp); if (rc) { printk(KERN_ERR "Failed to create sysfs attr group (%s)\n", s->pins[i].pin.name ); } grp_sd = sysfs_get_dirent(s->kobj.sd, NULL, s->pins[i].attr_grp.name); if (grp_sd == NULL) { printk(KERN_ERR "user-pins: failed to get sd for %s\n", s->pins[i].attr_grp.name); } else { s->pins[i].pin.sd = sysfs_get_dirent(grp_sd, NULL, "irq"); if ((s->pins[i].pin.sd == NULL) && (s->pins[i].pin.options & PIN_IRQ)) { printk(KERN_ERR "user-pins: failed to get sd for %s/irq\n", s->pins[i].attr_grp.name); } } } return 0; } static void pin_set_unregister(struct gpio_pin_set *s) { int i; if (s == NULL) { return; } /* for all pins */ for (i = 0; s->num_pins; i++) { if ((s->pins[i].pin.options & PIN_IRQ) && (s->pins[i].pin.irq_requested != 0)) { free_irq(gpio_to_irq(s->pins[i].pin.gpio), &s->pins[i].pin); } sysfs_remove_group(&s->kobj, &s->pins[i].attr_grp); gpio_free(s->pins[i].pin.gpio); } kobject_del(&s->kobj); kfree(s); } static int user_pins_probe(struct platform_device *pdev) { int i, rc; struct user_pins_platform_data *pdata; struct gpio_pin_dev_ctxt *dev_ctxt; pdata = pdev->dev.platform_data; if( pdata == NULL ) { return -ENODEV; } dev_ctxt = kzalloc(sizeof (struct gpio_pin_dev_ctxt) + pdata->num_sets * sizeof(struct gpio_pin_set *), GFP_KERNEL ); if (dev_ctxt == NULL) { return -ENOMEM; } dev_ctxt->num_sets = pdata->num_sets; for (i = 0; i < dev_ctxt->num_sets; i++) { dev_ctxt->sets[i] = pin_set_alloc (pdata->sets + i); if (dev_ctxt->sets[i] == NULL) { printk(KERN_ERR "Failed to init pin set '%s'\n", pdata->sets[i].set_name ); continue; } rc = pin_set_register(dev_ctxt->sets[i]); if (rc) { printk(KERN_ERR "Failed to register pin set '%s'\n", pdata->sets[i].set_name ); } } dev_set_drvdata(&pdev->dev, dev_ctxt); return 0; } /* * */ static int user_pins_remove(struct platform_device *pdev) { int i; struct gpio_pin_dev_ctxt *dev_ctxt; dev_ctxt = dev_get_drvdata(&pdev->dev); if (dev_ctxt == NULL) { return 0; } for (i = 0; i < dev_ctxt->num_sets; i++) { pin_set_unregister(dev_ctxt->sets[i]); } dev_set_drvdata(&pdev->dev, NULL); kfree(dev_ctxt); return 0; } #ifdef CONFIG_PM static int user_pins_suspend(struct platform_device *pdev, pm_message_t state) { int i, j; struct gpio_pin_dev_ctxt *dev_ctxt; struct gpio_pin_set *pset; struct gpio_pin *pin; unsigned long flags; dev_ctxt = platform_get_drvdata(pdev); if (dev_ctxt == NULL) { return 0; } spin_lock_irqsave ( &pins_lock, flags ); /* * The first loop is to check for any pending interrupts from * the time starting IRQ_HANDLE_OFF is set (from user space). * If so, fail the suspend, and let the system to go back up * to userspace. */ for( i = 0; i < dev_ctxt->num_sets; i++ ) { pset = dev_ctxt->sets[i]; for( j = 0; j < pset->num_pins; j++ ) { pin = &(pset->pins[j].pin); if ((pin->options & PIN_IRQ) && (pin->irq_handle_mode & IRQ_HANDLE_OFF) && (atomic_read(&pin->irqs_during_suspend) != 0)) { atomic_set(&pin->irqs_during_suspend, 0); spin_unlock_irqrestore ( &pins_lock, flags ); printk(KERN_INFO"%s: not suspending due to pending irqs for gpio %d\n", __func__, pin->gpio); return -EBUSY; } } } for (i = 0; i < dev_ctxt->num_sets; i++) { pset = dev_ctxt->sets[i]; for (j = 0; j < pset->num_pins; j++) { pin = &(pset->pins[j].pin); if (pin->options & PIN_WAKEUP_SOURCE) { int irq = gpio_to_irq(pin->gpio); if ((pin->options & PIN_IRQ) && pin->irq_requested && !pin->irq_masked) { disable_irq(gpio_to_irq(pin->gpio)); } enable_irq_wake(irq); } // If machine installed pinmux hook for the pin, call it // to mux it into suspended mode. Exception is made for // pins that are specifically configured to stay active // even during suspend periods. if (pin->pinmux && !pin->active_power_collapse) pin->pinmux(pin->gpio, PIN_MODE_SUSPENDED); } } spin_unlock_irqrestore ( &pins_lock, flags ); return 0; } static int user_pins_resume(struct platform_device *pdev) { int i, j; unsigned long flags; struct gpio_pin_dev_ctxt *dev_ctxt; struct gpio_pin_set *pset; struct gpio_pin *pin; dev_ctxt = platform_get_drvdata(pdev); if (dev_ctxt == NULL) { return 0; } spin_lock_irqsave(&pins_lock, flags); for (i = 0; i < dev_ctxt->num_sets; i++) { pset = dev_ctxt->sets[i]; for (j = 0; j < pset->num_pins; j++) { pin = &(pset->pins[j].pin); // If machine installed pinmux hook for the pin, call it // to mux it into active mode. If the pin is configured // to be active during suspend periods we assume we don't // need to remux it into active state again. if (pin->pinmux && !pin->active_power_collapse) pin->pinmux(pin->gpio, PIN_MODE_ACTIVE); if (pin->options & PIN_WAKEUP_SOURCE) { int irq = gpio_to_irq(pin->gpio); disable_irq_wake(irq); if ((pin->options & PIN_IRQ) && pin->irq_requested && !pin->irq_masked) { enable_irq(gpio_to_irq(pin->gpio)); } } atomic_set(&pin->irqs_during_suspend, 0); } } spin_unlock_irqrestore(&pins_lock, flags); return 0; } #else #define user_pins_suspend NULL #define user_pins_resume NULL #endif static struct platform_driver user_pins_driver = { .driver = { .name = DRIVER_NAME, }, .probe = user_pins_probe, .remove = __devexit_p(user_pins_remove), .suspend = user_pins_suspend, .resume = user_pins_resume, }; static int __init user_pins_init(void) { int rc; if (user_hw_kobj == NULL) { return -ENOMEM; } pins_kobj = kobject_create_and_add("pins", user_hw_kobj); if (pins_kobj == NULL) { return -ENOMEM; } /* register pins platform device */ rc = platform_driver_register(&user_pins_driver); if (rc) { kobject_del(pins_kobj); } user_pins_debug_init(); return rc; } static void __exit user_pins_exit(void) { kobject_del(pins_kobj); } module_init(user_pins_init); module_exit(user_pins_exit); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL");