#include #include #include #include #include #include #include #include #include "gpiolib.h" /*SWISTART*/ #ifdef CONFIG_SIERRA #include #include /* for RI PIN owner flag*/ #define RI_OWNER_MODEM 0 #define RI_OWNER_APP 1 static int gRmode = -1; static int gpio_ri = -1; #endif /*CONFIG_SIERRA*/ /*SWISTOP*/ static DEFINE_IDR(dirent_idr); /* lock protects against unexport_gpio() being called while * sysfs files are active. */ static DEFINE_MUTEX(sysfs_lock); /* * /sys/class/gpio/gpioN... only for GPIOs that are exported * /direction * * MAY BE OMITTED if kernel won't allow direction changes * * is read/write as "in" or "out" * * may also be written as "high" or "low", initializing * output value as specified ("out" implies "low") * /value * * always readable, subject to hardware behavior * * may be writable, as zero/nonzero * /edge * * configures behavior of poll(2) on /value * * available only if pin can generate IRQs on input * * is read/write as "none", "falling", "rising", or "both" * /active_low * * configures polarity of /value * * is read/write as zero/nonzero * * also affects existing and subsequent "falling" and "rising" * /edge configuration */ #ifdef CONFIG_SIERRA /* Product specific assignments in gpiolib_sysfs_init() */ static struct ext_gpio_map *ext_gpio = NULL; static struct gpio_chip gpio_ext_chip = { .label = "msmextgpio", .base = 1, }; /* GPIOs in this table must only be given the FUNCTION_EMBEDDED_HOST * property by default if they are not configurable through other methods. */ static struct ext_gpio_map ext_gpio_ar[]={ {"1", 15,FUNCTION_UNALLOCATED}, {"2", 14,FUNCTION_UNALLOCATED}, {"3", 13,FUNCTION_UNALLOCATED}, {"4", 12,FUNCTION_UNALLOCATED}, {"5", 30,FUNCTION_EMBEDDED_HOST}, {"6", 16,FUNCTION_UNALLOCATED}, {"7", 17,FUNCTION_UNALLOCATED}, {"8", 43,FUNCTION_UNALLOCATED}, {"9", 49,FUNCTION_UNALLOCATED}, {"10", 50,FUNCTION_UNALLOCATED}, {"11", 45,FUNCTION_UNALLOCATED}, {"12", 75,FUNCTION_UNALLOCATED}, {"13", 37,FUNCTION_UNALLOCATED}, {"14", 36,FUNCTION_UNALLOCATED}, {"15", -1,FUNCTION_EMBEDDED_HOST}, {"16", -1,FUNCTION_EMBEDDED_HOST}, {"17", -1,FUNCTION_EMBEDDED_HOST}, {"18", -1,FUNCTION_EMBEDDED_HOST}, {"19", -1,FUNCTION_EMBEDDED_HOST}, {"20", -1,FUNCTION_EMBEDDED_HOST}, {"21", -1,FUNCTION_EMBEDDED_HOST}, {"22", -1,FUNCTION_EMBEDDED_HOST}, {"23", -1,FUNCTION_EMBEDDED_HOST}, {"24", -1,FUNCTION_EMBEDDED_HOST}, {"25", 26,FUNCTION_EMBEDDED_HOST}, {"26", 44,FUNCTION_EMBEDDED_HOST}, {"27", -1,FUNCTION_EMBEDDED_HOST}, {"28", -1,FUNCTION_EMBEDDED_HOST}, {"29", 74,FUNCTION_EMBEDDED_HOST}, {"30", -1,FUNCTION_EMBEDDED_HOST}, {"31", -1,FUNCTION_EMBEDDED_HOST}, {"32", -1,FUNCTION_EMBEDDED_HOST}, {"33", -1,FUNCTION_EMBEDDED_HOST}, {"34", 11,FUNCTION_EMBEDDED_HOST}, {"35", 10,FUNCTION_EMBEDDED_HOST}, {"36", -1,FUNCTION_EMBEDDED_HOST}, {"37", -1,FUNCTION_EMBEDDED_HOST}, {"38", -1,FUNCTION_EMBEDDED_HOST}, {"39", -1,FUNCTION_EMBEDDED_HOST}, {"40", -1,FUNCTION_EMBEDDED_HOST}, {"41", -1,FUNCTION_EMBEDDED_HOST}, {"42", -1,FUNCTION_EMBEDDED_HOST}, {"43", -1,FUNCTION_EMBEDDED_HOST}, {"44", -1,FUNCTION_EMBEDDED_HOST}, {"45", -1,FUNCTION_EMBEDDED_HOST}, /* SWI_TBD [Kinbo:2016-07-27]: QTI9X28-312, GPIO46 is used by HSIC, but control HSIC need GPIO API*/ {"46", 59,FUNCTION_UNALLOCATED}, {"M1", 1020,FUNCTION_UNALLOCATED}, {"M2", 1021,FUNCTION_UNALLOCATED}, {"M3", 1023,FUNCTION_UNALLOCATED}, {"M4", 1022,FUNCTION_UNALLOCATED}, {GPIO_NAME_RI,35,FUNCTION_UNALLOCATED} }; /* GPIOs in this table must only be given the FUNCTION_EMBEDDED_HOST * property by default if they are not configurable through other methods. */ static struct ext_gpio_map ext_gpio_wp[]={ {"1", -1,FUNCTION_UNALLOCATED}, {"2", 38,FUNCTION_UNALLOCATED}, {"3", -1,FUNCTION_UNALLOCATED}, {"4", 30,FUNCTION_UNALLOCATED}, {"5", -1,FUNCTION_UNALLOCATED}, {"6", 1022,FUNCTION_UNALLOCATED}, {"7", 16,FUNCTION_UNALLOCATED}, {"8", 58,FUNCTION_UNALLOCATED}, {"9", -1,FUNCTION_UNALLOCATED}, {"10", -1,FUNCTION_UNALLOCATED}, {"11", -1,FUNCTION_UNALLOCATED}, {"12", -1,FUNCTION_UNALLOCATED}, {"13", 76,FUNCTION_UNALLOCATED}, {"14", -1,FUNCTION_UNALLOCATED}, {"15", -1,FUNCTION_UNALLOCATED}, {"16", -1,FUNCTION_UNALLOCATED}, {"17", -1,FUNCTION_UNALLOCATED}, {"18", -1,FUNCTION_UNALLOCATED}, {"19", -1,FUNCTION_UNALLOCATED}, {"20", -1,FUNCTION_UNALLOCATED}, {"21", 8,FUNCTION_UNALLOCATED}, {"22", 9,FUNCTION_UNALLOCATED}, {"23", 10,FUNCTION_UNALLOCATED}, {"24", 11,FUNCTION_UNALLOCATED}, {"25", 51,FUNCTION_UNALLOCATED}, {"26", -1,FUNCTION_UNALLOCATED}, {"27", -1,FUNCTION_UNALLOCATED}, {"28", 45,FUNCTION_UNALLOCATED}, {"29", 46,FUNCTION_UNALLOCATED}, {"30", 47,FUNCTION_UNALLOCATED}, {"31", 48,FUNCTION_UNALLOCATED}, {"32", 77,FUNCTION_UNALLOCATED}, {"33", 78,FUNCTION_UNALLOCATED}, {"34", -1,FUNCTION_UNALLOCATED}, {"35", 37,FUNCTION_UNALLOCATED}, #ifdef CONFIG_GPIO_SWIMCU {"36", SWIMCU_GPIO_TO_SYS(0),FUNCTION_EMBEDDED_HOST}, {"37", SWIMCU_GPIO_TO_SYS(1),FUNCTION_EMBEDDED_HOST}, {"38", SWIMCU_GPIO_TO_SYS(2),FUNCTION_EMBEDDED_HOST}, {"39", -1,FUNCTION_UNALLOCATED}, {"40", SWIMCU_GPIO_TO_SYS(3),FUNCTION_EMBEDDED_HOST}, {"41", SWIMCU_GPIO_TO_SYS(4),FUNCTION_EMBEDDED_HOST}, #else /* CONFIG_GPIO_SWIMCU */ {"36", -1,FUNCTION_UNALLOCATED}, {"37", -1,FUNCTION_UNALLOCATED}, {"38", -1,FUNCTION_UNALLOCATED}, {"39", -1,FUNCTION_UNALLOCATED}, {"40", -1,FUNCTION_UNALLOCATED}, {"41", -1,FUNCTION_UNALLOCATED}, #endif /* !CONFIG_GPIO_SWIMCU */ {"42", 79,FUNCTION_UNALLOCATED}, {"43", -1,FUNCTION_UNALLOCATED}, {"44", -1,FUNCTION_UNALLOCATED}, {"45", -1,FUNCTION_UNALLOCATED}, {"46", -1,FUNCTION_UNALLOCATED}, {"M1", -1,FUNCTION_UNALLOCATED}, {"M2", -1,FUNCTION_UNALLOCATED}, {"M3", -1,FUNCTION_UNALLOCATED}, {"M4", -1,FUNCTION_UNALLOCATED}, {GPIO_NAME_RI,25,FUNCTION_UNALLOCATED}, //DTR {"101",17,FUNCTION_EMBEDDED_HOST}, //DCD {"102",24,FUNCTION_EMBEDDED_HOST}, //DSR {"103",36,FUNCTION_EMBEDDED_HOST} }; /* GPIOs in this table must only be given the FUNCTION_EMBEDDED_HOST * property by default if they are not configurable through other methods. */ static struct ext_gpio_map ext_gpio_mft[]={ {"0", 0,FUNCTION_UNALLOCATED}, {"1", 1,FUNCTION_UNALLOCATED}, {"2", 2,FUNCTION_UNALLOCATED}, {"3", 3,FUNCTION_UNALLOCATED}, {"4", 4,FUNCTION_UNALLOCATED}, {"5", 5,FUNCTION_UNALLOCATED}, {"6", 6,FUNCTION_UNALLOCATED}, {"7", 7,FUNCTION_UNALLOCATED}, {"8", 8,FUNCTION_UNALLOCATED}, {"9", 9,FUNCTION_UNALLOCATED}, {"10", 10,FUNCTION_UNALLOCATED}, {"11", 11,FUNCTION_UNALLOCATED}, {"12", 12,FUNCTION_UNALLOCATED}, {"13", 13,FUNCTION_UNALLOCATED}, {"14", 14,FUNCTION_UNALLOCATED}, {"15", 15,FUNCTION_UNALLOCATED}, {"16", 16,FUNCTION_UNALLOCATED}, {"17", 17,FUNCTION_UNALLOCATED}, {"18" ,18,FUNCTION_UNALLOCATED}, {"19", 19,FUNCTION_UNALLOCATED}, {"20", 20,FUNCTION_UNALLOCATED}, {"21", 21,FUNCTION_UNALLOCATED}, {"22", 22,FUNCTION_UNALLOCATED}, {"23", 23,FUNCTION_UNALLOCATED}, {"24", 24,FUNCTION_UNALLOCATED}, {"25", 25,FUNCTION_UNALLOCATED}, {"26", 26,FUNCTION_UNALLOCATED}, {"27", 27,FUNCTION_UNALLOCATED}, {"28", 28,FUNCTION_UNALLOCATED}, {"29", 29,FUNCTION_UNALLOCATED}, {"30", 30,FUNCTION_UNALLOCATED}, {"31", 31,FUNCTION_UNALLOCATED}, {"32", 32,FUNCTION_UNALLOCATED}, {"33", 33,FUNCTION_UNALLOCATED}, {"34", 34,FUNCTION_UNALLOCATED}, {"35", 35,FUNCTION_UNALLOCATED}, {"36", 36,FUNCTION_UNALLOCATED}, {"37", 37,FUNCTION_UNALLOCATED}, {"38" ,38,FUNCTION_UNALLOCATED}, {"39", 39,FUNCTION_UNALLOCATED}, {"40", 40,FUNCTION_UNALLOCATED}, {"41", 41,FUNCTION_UNALLOCATED}, {"42", 42,FUNCTION_UNALLOCATED}, {"43", 43,FUNCTION_UNALLOCATED}, {"44", 44,FUNCTION_UNALLOCATED}, {"45", 45,FUNCTION_UNALLOCATED}, {"46", 46,FUNCTION_UNALLOCATED}, {"47", 47,FUNCTION_UNALLOCATED}, {"48", 48,FUNCTION_UNALLOCATED}, {"49", 49,FUNCTION_UNALLOCATED}, {"50", 50,FUNCTION_UNALLOCATED}, {"51", 51,FUNCTION_UNALLOCATED}, {"52", 52,FUNCTION_UNALLOCATED}, {"53", 53,FUNCTION_UNALLOCATED}, {"54", 54,FUNCTION_UNALLOCATED}, {"55", 55,FUNCTION_UNALLOCATED}, {"56", 56,FUNCTION_UNALLOCATED}, {"57", 57,FUNCTION_UNALLOCATED}, {"58" ,58,FUNCTION_UNALLOCATED}, {"59", 59,FUNCTION_UNALLOCATED}, {"60", 60,FUNCTION_UNALLOCATED}, {"61", 61,FUNCTION_UNALLOCATED}, {"62", 62,FUNCTION_UNALLOCATED}, {"63", 63,FUNCTION_UNALLOCATED}, {"64", 64,FUNCTION_UNALLOCATED}, {"65", 65,FUNCTION_UNALLOCATED}, {"66", 66,FUNCTION_UNALLOCATED}, {"67", 67,FUNCTION_UNALLOCATED}, {"68" ,68,FUNCTION_UNALLOCATED}, {"69", 69,FUNCTION_UNALLOCATED}, {"70", 70,FUNCTION_UNALLOCATED}, {"71", 71,FUNCTION_UNALLOCATED}, {"72", 72,FUNCTION_UNALLOCATED}, {"73", 73,FUNCTION_UNALLOCATED}, {"74", 74,FUNCTION_UNALLOCATED}, {"75", 75,FUNCTION_UNALLOCATED}, {"76", 76,FUNCTION_UNALLOCATED}, {"77", 77,FUNCTION_UNALLOCATED}, {"78", 78,FUNCTION_UNALLOCATED}, {"79", 79,FUNCTION_UNALLOCATED}, {"80", 1020,FUNCTION_UNALLOCATED}, {"81", 1021,FUNCTION_UNALLOCATED}, {"82", 1023,FUNCTION_UNALLOCATED}, {"83", 1022,FUNCTION_UNALLOCATED} }; /** * getap_multiplex_gpio() - set the gpio ownership/function in AP * based on the mask retrieved from modem * * Returns nothing * */ static void getap_multiplex_gpio(void) { int i; for(i = 0; i < gpio_ext_chip.ngpio; i++) { if (gpio_ext_chip.mask & (0x1ULL << i)) ext_gpio[i].function = FUNCTION_EMBEDDED_HOST; } } /** * gpio_map_name_to_num() - Return the internal GPIO number for an * external GPIO name * @*buf: The external GPIO name (may include a trailing ) * @*alias: pointer to return whether this name is an alias for another table entry * Context: After gpiolib_sysfs_init has setup the gpio device * * Returns a negative number if the gpio_name is not mapped to a number * or if the access to the GPIO is prohibited. * */ static int gpio_map_name_to_num(const char *buf, bool *alias) { int i; int gpio_num = -1; char gpio_name[GPIO_NAME_MAX+1]; int len; len = min( strlen(buf), sizeof(gpio_name)-1 ); memcpy(gpio_name, buf, len); if ((len > 0) && (gpio_name[len-1] < 0x20)) len--; /* strip trailing <0x0a> from buf for compare ops */ gpio_name[len] = 0; if (ext_gpio != NULL) { for(i = 0; i < gpio_ext_chip.ngpio; i++) { if( strncasecmp( gpio_name, ext_gpio[i].gpio_name, GPIO_NAME_MAX ) == 0 ) { /* the multi-function GPIO is used as another feature, cannot export */ if(FUNCTION_EMBEDDED_HOST != ext_gpio[i].function) { return -1; } gpio_num = ext_gpio[i].gpio_num; pr_debug("%s: find GPIO %d\n", __func__, gpio_num); return gpio_num; } } } pr_debug("%s: Can not find GPIO %s\n", __func__, gpio_name); return -1; } /** * gpio_map_num_to_name() - Return the external GPIO name for an * internal GPIO number * @gpio_num: The internal (i.e. MDM) GPIO pin number * @alias: Return the second entry if 2 names are mapped to the same internal GPIO number * Context: After gpiolib_sysfs_init has setup the gpio device * * Returns NULL if the gpio_num is not mapped to a name * or if the access to the GPIO is prohibited. * */ static char *gpio_map_num_to_name(int gpio_num, bool alias) { int i; if (ext_gpio != NULL) { for(i = 0; i < gpio_ext_chip.ngpio; i++) { if(gpio_num == ext_gpio[i].gpio_num) { if(FUNCTION_EMBEDDED_HOST != ext_gpio[i].function) { return NULL; } return ext_gpio[i].gpio_name; } } } pr_debug("%s: Can not find GPIO %d\n", __func__, gpio_num); return NULL; } /** * gpio_sync_ri() - sync gpio RI function with riowner * Context: After ext_gpio and gpio_ri have been set. * * Returns 1 if apps, 0 if modem, or -1 if RI not found. */ static int gpio_sync_ri(void) { int ri_owner = -1; if (gpio_ri >= 0) { /* Check if RI gpio is owned by APP core * In this case, set that gpio for RI management * RI owner: 1 APP , 0 Modem. See AT!RIOWNER */ ri_owner = bsgetriowner(); if (RI_OWNER_APP == ri_owner) { pr_debug("%s: RI owner is APP\n", __func__); ext_gpio[gpio_ri].function = FUNCTION_EMBEDDED_HOST; } else { pr_debug("%s: RI owner is Modem\n", __func__); ext_gpio[gpio_ri].function = FUNCTION_UNALLOCATED; } } return ri_owner; } /** * gpio_set_map_table() * * Purpose: set gpio mapping table according smem * * Returns: no return */ static void gpio_set_map_table( void ) { gRmode = sierra_smem_get_factory_mode(); if(gRmode == 1) { ext_gpio = ext_gpio_mft; gpio_ext_chip.ngpio = NR_EXT_GPIOS_MFT; } else { if ( bs_support_get (BSFEATURE_WP) ) { gpio_sync_ri(); ext_gpio = ext_gpio_wp; gpio_ext_chip.ngpio = NR_EXT_GPIOS_WP; } else { ext_gpio = ext_gpio_ar; gpio_ext_chip.ngpio = NR_EXT_GPIOS_AR; } } } #endif /*CONFIG_SIERRA*/ static ssize_t gpio_direction_show(struct device *dev, struct device_attribute *attr, char *buf) { const struct gpio_desc *desc = dev_get_drvdata(dev); ssize_t status; mutex_lock(&sysfs_lock); if (!test_bit(FLAG_EXPORT, &desc->flags)) { status = -EIO; } else { gpiod_get_direction(desc); status = sprintf(buf, "%s\n", test_bit(FLAG_IS_OUT, &desc->flags) ? "out" : "in"); } mutex_unlock(&sysfs_lock); return status; } static ssize_t gpio_direction_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct gpio_desc *desc = dev_get_drvdata(dev); ssize_t status; mutex_lock(&sysfs_lock); if (!test_bit(FLAG_EXPORT, &desc->flags)) status = -EIO; else if (sysfs_streq(buf, "high")) status = gpiod_direction_output_raw(desc, 1); else if (sysfs_streq(buf, "out") || sysfs_streq(buf, "low")) status = gpiod_direction_output_raw(desc, 0); else if (sysfs_streq(buf, "in")) status = gpiod_direction_input(desc); else status = -EINVAL; mutex_unlock(&sysfs_lock); return status ? : size; } static /* const */ DEVICE_ATTR(direction, 0644, gpio_direction_show, gpio_direction_store); /* SWISTART */ #ifdef CONFIG_SIERRA static ssize_t gpio_pull_show(struct device *dev, struct device_attribute *attr, char *buf) { const struct gpio_desc *desc = dev_get_drvdata(dev); ssize_t status; mutex_lock(&sysfs_lock); if (!test_bit(FLAG_EXPORT, &desc->flags)) status = -EIO; else status = sprintf(buf, "%s\n", test_bit(FLAG_IS_UP, &desc->flags) ? "up" : "down"); mutex_unlock(&sysfs_lock); return status; } static ssize_t gpio_pull_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { const struct gpio_desc *desc = dev_get_drvdata(dev); ssize_t status; mutex_lock(&sysfs_lock); if (!test_bit(FLAG_EXPORT, &desc->flags)) status = -EIO; else if (sysfs_streq(buf, "up")) status = gpio_pull_up((struct gpio_desc *)desc); else if (sysfs_streq(buf, "down")) status = gpio_pull_down((struct gpio_desc *)desc); else status = -EINVAL; mutex_unlock(&sysfs_lock); return status ? : size; } static /* const */ DEVICE_ATTR(pull, 0644, gpio_pull_show, gpio_pull_store); #endif /* SWISTOP */ static ssize_t gpio_value_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gpio_desc *desc = dev_get_drvdata(dev); ssize_t status; mutex_lock(&sysfs_lock); if (!test_bit(FLAG_EXPORT, &desc->flags)) status = -EIO; else status = sprintf(buf, "%d\n", gpiod_get_value_cansleep(desc)); mutex_unlock(&sysfs_lock); return status; } static ssize_t gpio_value_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct gpio_desc *desc = dev_get_drvdata(dev); ssize_t status; mutex_lock(&sysfs_lock); if (!test_bit(FLAG_EXPORT, &desc->flags)) status = -EIO; else if (!test_bit(FLAG_IS_OUT, &desc->flags)) status = -EPERM; else { long value; status = kstrtol(buf, 0, &value); if (status == 0) { gpiod_set_value_cansleep(desc, value); status = size; } } mutex_unlock(&sysfs_lock); return status; } static DEVICE_ATTR(value, 0644, gpio_value_show, gpio_value_store); static irqreturn_t gpio_sysfs_irq(int irq, void *priv) { struct kernfs_node *value_sd = priv; sysfs_notify_dirent(value_sd); return IRQ_HANDLED; } static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev, unsigned long gpio_flags) { struct kernfs_node *value_sd; unsigned long irq_flags; int ret, irq, id; if ((desc->flags & GPIO_TRIGGER_MASK) == gpio_flags) return 0; irq = gpiod_to_irq(desc); if (irq < 0) return -EIO; id = desc->flags >> ID_SHIFT; value_sd = idr_find(&dirent_idr, id); if (value_sd) free_irq(irq, value_sd); desc->flags &= ~GPIO_TRIGGER_MASK; if (!gpio_flags) { #ifdef CONFIG_SIERRA if(test_bit(FLAG_IRQ_WAKEUP, &desc->flags)) { clear_bit(FLAG_IRQ_WAKEUP, &desc->flags); irq_set_irq_wake(irq, 0); } #endif gpio_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc)); ret = 0; goto free_id; } irq_flags = IRQF_SHARED; if (test_bit(FLAG_TRIG_FALL, &gpio_flags)) irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; if (test_bit(FLAG_TRIG_RISE, &gpio_flags)) irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; if (!value_sd) { value_sd = sysfs_get_dirent(dev->kobj.sd, "value"); if (!value_sd) { ret = -ENODEV; goto err_out; } ret = idr_alloc(&dirent_idr, value_sd, 1, 0, GFP_KERNEL); if (ret < 0) goto free_sd; id = ret; desc->flags &= GPIO_FLAGS_MASK; desc->flags |= (unsigned long)id << ID_SHIFT; if (desc->flags >> ID_SHIFT != id) { ret = -ERANGE; goto free_id; } } ret = request_any_context_irq(irq, gpio_sysfs_irq, irq_flags, "gpiolib", value_sd); if (ret < 0) goto free_id; ret = gpio_lock_as_irq(desc->chip, gpio_chip_hwgpio(desc)); if (ret < 0) { gpiod_warn(desc, "failed to flag the GPIO for IRQ\n"); goto free_id; } desc->flags |= gpio_flags; #ifdef CONFIG_SIERRA if(!test_bit(FLAG_IRQ_WAKEUP, &desc->flags)) { set_bit(FLAG_IRQ_WAKEUP, &desc->flags); irq_set_irq_wake(irq, 1); } #endif return 0; free_id: idr_remove(&dirent_idr, id); desc->flags &= GPIO_FLAGS_MASK; free_sd: if (value_sd) sysfs_put(value_sd); err_out: return ret; } static const struct { const char *name; unsigned long flags; } trigger_types[] = { { "none", 0 }, { "falling", BIT(FLAG_TRIG_FALL) }, { "rising", BIT(FLAG_TRIG_RISE) }, { "both", BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE) }, }; static ssize_t gpio_edge_show(struct device *dev, struct device_attribute *attr, char *buf) { const struct gpio_desc *desc = dev_get_drvdata(dev); ssize_t status; mutex_lock(&sysfs_lock); if (!test_bit(FLAG_EXPORT, &desc->flags)) status = -EIO; else { int i; status = 0; for (i = 0; i < ARRAY_SIZE(trigger_types); i++) if ((desc->flags & GPIO_TRIGGER_MASK) == trigger_types[i].flags) { status = sprintf(buf, "%s\n", trigger_types[i].name); break; } } mutex_unlock(&sysfs_lock); return status; } static ssize_t gpio_edge_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct gpio_desc *desc = dev_get_drvdata(dev); ssize_t status; int i; for (i = 0; i < ARRAY_SIZE(trigger_types); i++) if (sysfs_streq(trigger_types[i].name, buf)) goto found; return -EINVAL; found: mutex_lock(&sysfs_lock); if (!test_bit(FLAG_EXPORT, &desc->flags)) status = -EIO; else { status = gpio_setup_irq(desc, dev, trigger_types[i].flags); if (!status) status = size; } mutex_unlock(&sysfs_lock); return status; } static DEVICE_ATTR(edge, 0644, gpio_edge_show, gpio_edge_store); static int sysfs_set_active_low(struct gpio_desc *desc, struct device *dev, int value) { int status = 0; if (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) == !!value) return 0; if (value) set_bit(FLAG_ACTIVE_LOW, &desc->flags); else clear_bit(FLAG_ACTIVE_LOW, &desc->flags); /* reconfigure poll(2) support if enabled on one edge only */ if (dev != NULL && (!!test_bit(FLAG_TRIG_RISE, &desc->flags) ^ !!test_bit(FLAG_TRIG_FALL, &desc->flags))) { unsigned long trigger_flags = desc->flags & GPIO_TRIGGER_MASK; gpio_setup_irq(desc, dev, 0); status = gpio_setup_irq(desc, dev, trigger_flags); } return status; } static ssize_t gpio_active_low_show(struct device *dev, struct device_attribute *attr, char *buf) { const struct gpio_desc *desc = dev_get_drvdata(dev); ssize_t status; mutex_lock(&sysfs_lock); if (!test_bit(FLAG_EXPORT, &desc->flags)) status = -EIO; else status = sprintf(buf, "%d\n", !!test_bit(FLAG_ACTIVE_LOW, &desc->flags)); mutex_unlock(&sysfs_lock); return status; } static ssize_t gpio_active_low_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct gpio_desc *desc = dev_get_drvdata(dev); ssize_t status; mutex_lock(&sysfs_lock); if (!test_bit(FLAG_EXPORT, &desc->flags)) { status = -EIO; } else { long value; status = kstrtol(buf, 0, &value); if (status == 0) status = sysfs_set_active_low(desc, dev, value != 0); } mutex_unlock(&sysfs_lock); return status ? : size; } static DEVICE_ATTR(active_low, 0644, gpio_active_low_show, gpio_active_low_store); static struct attribute *gpio_attrs[] = { &dev_attr_value.attr, &dev_attr_active_low.attr, /* SWISTART */ #ifdef CONFIG_SIERRA &dev_attr_pull.attr, #endif /* SWISTART */ NULL, }; ATTRIBUTE_GROUPS(gpio); /* * /sys/class/gpio/gpiochipN/ * /base ... matching gpio_chip.base (N) * /label ... matching gpio_chip.label * /ngpio ... matching gpio_chip.ngpio */ static ssize_t chip_base_show(struct device *dev, struct device_attribute *attr, char *buf) { const struct gpio_chip *chip = dev_get_drvdata(dev); return sprintf(buf, "%d\n", chip->base); } static DEVICE_ATTR(base, 0444, chip_base_show, NULL); static ssize_t chip_label_show(struct device *dev, struct device_attribute *attr, char *buf) { const struct gpio_chip *chip = dev_get_drvdata(dev); return sprintf(buf, "%s\n", chip->label ? : ""); } static DEVICE_ATTR(label, 0444, chip_label_show, NULL); static ssize_t chip_ngpio_show(struct device *dev, struct device_attribute *attr, char *buf) { const struct gpio_chip *chip = dev_get_drvdata(dev); return sprintf(buf, "%u\n", chip->ngpio); } static DEVICE_ATTR(ngpio, 0444, chip_ngpio_show, NULL); /*SWISTART*/ #ifdef CONFIG_SIERRA static ssize_t chip_mask_show(struct device *dev, struct device_attribute *attr, char *buf) { const struct gpio_chip *chip = dev_get_drvdata(dev); return sprintf(buf, "0x%08x%08x\n", (u32)(chip->mask>>32)&0xFFFFFFFF, (u32)chip->mask&0xFFFFFFFF); } static DEVICE_ATTR(mask, 0444, chip_mask_show, NULL); #endif /*SWISTOP*/ static struct attribute *gpiochip_attrs[] = { &dev_attr_base.attr, &dev_attr_label.attr, &dev_attr_ngpio.attr, /*SWISTART*/ #ifdef CONFIG_SIERRA &dev_attr_mask.attr, #endif /*SWISTOP*/ NULL, }; ATTRIBUTE_GROUPS(gpiochip); /* * /sys/class/gpio/export ... write-only * integer N ... number of GPIO to export (full access) * /sys/class/gpio/unexport ... write-only * integer N ... number of GPIO to unexport */ static ssize_t export_store(struct class *class, struct class_attribute *attr, const char *buf, size_t len) { long gpio; struct gpio_desc *desc; int status; #ifdef CONFIG_SIERRA bool alias = false; gpio_set_map_table(); status = gpio = gpio_map_name_to_num(buf, &alias); pr_debug("%s: Export GPIO: %ld\n", __func__,gpio); #else status = kstrtol(buf, 0, &gpio); #endif /*CONFIG_SIERRA*/ if (status < 0) goto done; desc = gpio_to_desc(gpio); /* reject invalid GPIOs */ if (!desc) { pr_warn("%s: Invalid GPIO %ld\n", __func__, gpio); return -EINVAL; } /* No extra locking here; FLAG_SYSFS just signifies that the * request and export were done by on behalf of userspace, so * they may be undone on its behalf too. */ status = gpiod_request(desc, "sysfs"); if (status < 0) { if (status == -EPROBE_DEFER) status = -ENODEV; goto done; } status = gpiod_export(desc, true); if (status < 0) gpiod_free(desc); else set_bit(FLAG_SYSFS, &desc->flags); done: if (status) pr_debug("%s: Status %d\n", __func__, status); return status ? : len; } static ssize_t unexport_store(struct class *class, struct class_attribute *attr, const char *buf, size_t len) { long gpio; struct gpio_desc *desc; int status; #ifdef CONFIG_SIERRA bool alias = false; gpio_set_map_table(); status = gpio = gpio_map_name_to_num(buf, &alias); pr_debug("%s: Uexport GPIO: %ld\n", __func__,gpio); #else status = kstrtol(buf, 0, &gpio); #endif /*CONFIG_SIERRA*/ if (status < 0) goto done; desc = gpio_to_desc(gpio); /* reject bogus commands (gpio_unexport ignores them) */ if (!desc) { pr_warn("%s: Invalid GPIO %ld\n", __func__, gpio); return -EINVAL; } status = -EINVAL; /* No extra locking here; FLAG_SYSFS just signifies that the * request and export were done by on behalf of userspace, so * they may be undone on its behalf too. */ if (test_and_clear_bit(FLAG_SYSFS, &desc->flags)) { status = 0; gpiod_free(desc); } done: if (status) pr_debug("%s: Status %d\n", __func__, status); return status ? : len; } static struct class_attribute gpio_class_attrs[] = { __ATTR(export, 0200, NULL, export_store), __ATTR(unexport, 0200, NULL, unexport_store), __ATTR_NULL, }; static struct class gpio_class = { .name = "gpio", .owner = THIS_MODULE, .class_attrs = gpio_class_attrs, }; /** * gpiod_export - export a GPIO through sysfs * @gpio: gpio to make available, already requested * @direction_may_change: true if userspace may change gpio direction * Context: arch_initcall or later * * When drivers want to make a GPIO accessible to userspace after they * have requested it -- perhaps while debugging, or as part of their * public interface -- they may use this routine. If the GPIO can * change direction (some can't) and the caller allows it, userspace * will see "direction" sysfs attribute which may be used to change * the gpio's direction. A "value" attribute will always be provided. * * Returns zero on success, else an error. */ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { struct gpio_chip *chip; unsigned long flags; int status; const char *ioname = NULL; struct device *dev; int offset; /*SWISTART*/ #ifdef CONFIG_SIERRA char ioname_buf[IONAME_MAX+1] = IONAME_PREFIX; #endif /*SWISTOP*/ /* can't export until sysfs is available ... */ if (!gpio_class.p) { pr_debug("%s: called too early!\n", __func__); return -ENOENT; } if (!desc) { pr_debug("%s: invalid gpio descriptor\n", __func__); return -EINVAL; } chip = desc->chip; mutex_lock(&sysfs_lock); /* check if chip is being removed */ if (!chip || !chip->exported) { status = -ENODEV; goto fail_unlock; } spin_lock_irqsave(&gpio_lock, flags); if (!test_bit(FLAG_REQUESTED, &desc->flags) || test_bit(FLAG_EXPORT, &desc->flags)) { spin_unlock_irqrestore(&gpio_lock, flags); gpiod_dbg(desc, "%s: unavailable (requested=%d, exported=%d)\n", __func__, test_bit(FLAG_REQUESTED, &desc->flags), test_bit(FLAG_EXPORT, &desc->flags)); status = -EPERM; goto fail_unlock; } if (!desc->chip->direction_input || !desc->chip->direction_output) direction_may_change = false; spin_unlock_irqrestore(&gpio_lock, flags); offset = gpio_chip_hwgpio(desc); if (desc->chip->names && desc->chip->names[offset]) ioname = desc->chip->names[offset]; /*SWISTART*/ #ifdef CONFIG_SIERRA gpio_set_map_table(); strncat(ioname_buf, gpio_map_num_to_name(desc_to_gpio(desc), false), GPIO_NAME_MAX); ioname = ioname_buf; pr_debug("%s: sierra--find GPIO,chipdev = %d,chipngpio = %d,chipbase = %d\n", __func__, (int)desc->chip->dev, (int)desc->chip->ngpio, (int)desc->chip->base); #endif /*CONFIG_SIERRA*/ /*SWISTOP*/ dev = device_create_with_groups(&gpio_class, desc->chip->dev, MKDEV(0, 0), desc, gpio_groups, ioname ? ioname : "gpio%u", desc_to_gpio(desc)); if (IS_ERR(dev)) { status = PTR_ERR(dev); goto fail_unlock; } if (direction_may_change) { status = device_create_file(dev, &dev_attr_direction); if (status) goto fail_unregister_device; } if (gpiod_to_irq(desc) >= 0 && (direction_may_change || !test_bit(FLAG_IS_OUT, &desc->flags))) { status = device_create_file(dev, &dev_attr_edge); if (status) goto fail_remove_attr_direction; } set_bit(FLAG_EXPORT, &desc->flags); mutex_unlock(&sysfs_lock); return 0; fail_remove_attr_direction: device_remove_file(dev, &dev_attr_direction); fail_unregister_device: device_unregister(dev); fail_unlock: mutex_unlock(&sysfs_lock); gpiod_dbg(desc, "%s: status %d\n", __func__, status); return status; } EXPORT_SYMBOL_GPL(gpiod_export); static int match_export(struct device *dev, const void *data) { return dev_get_drvdata(dev) == data; } /** * gpiod_export_link - create a sysfs link to an exported GPIO node * @dev: device under which to create symlink * @name: name of the symlink * @gpio: gpio to create symlink to, already exported * * Set up a symlink from /sys/.../dev/name to /sys/class/gpio/gpioN * node. Caller is responsible for unlinking. * * Returns zero on success, else an error. */ int gpiod_export_link(struct device *dev, const char *name, struct gpio_desc *desc) { int status = -EINVAL; if (!desc) { pr_warn("%s: invalid GPIO\n", __func__); return -EINVAL; } mutex_lock(&sysfs_lock); if (test_bit(FLAG_EXPORT, &desc->flags)) { struct device *tdev; tdev = class_find_device(&gpio_class, NULL, desc, match_export); if (tdev != NULL) { status = sysfs_create_link(&dev->kobj, &tdev->kobj, name); put_device(tdev); } else { status = -ENODEV; } } mutex_unlock(&sysfs_lock); if (status) gpiod_dbg(desc, "%s: status %d\n", __func__, status); return status; } EXPORT_SYMBOL_GPL(gpiod_export_link); /** * gpiod_sysfs_set_active_low - set the polarity of gpio sysfs value * @gpio: gpio to change * @value: non-zero to use active low, i.e. inverted values * * Set the polarity of /sys/class/gpio/gpioN/value sysfs attribute. * The GPIO does not have to be exported yet. If poll(2) support has * been enabled for either rising or falling edge, it will be * reconfigured to follow the new polarity. * * Returns zero on success, else an error. */ int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value) { struct device *dev = NULL; int status = -EINVAL; if (!desc) { pr_warn("%s: invalid GPIO\n", __func__); return -EINVAL; } mutex_lock(&sysfs_lock); if (test_bit(FLAG_EXPORT, &desc->flags)) { dev = class_find_device(&gpio_class, NULL, desc, match_export); if (dev == NULL) { status = -ENODEV; goto unlock; } } status = sysfs_set_active_low(desc, dev, value); put_device(dev); unlock: mutex_unlock(&sysfs_lock); if (status) gpiod_dbg(desc, "%s: status %d\n", __func__, status); return status; } EXPORT_SYMBOL_GPL(gpiod_sysfs_set_active_low); /** * gpiod_unexport - reverse effect of gpio_export() * @gpio: gpio to make unavailable * * This is implicit on gpio_free(). */ void gpiod_unexport(struct gpio_desc *desc) { int status = 0; struct device *dev = NULL; if (!desc) { pr_warn("%s: invalid GPIO\n", __func__); return; } mutex_lock(&sysfs_lock); if (test_bit(FLAG_EXPORT, &desc->flags)) { dev = class_find_device(&gpio_class, NULL, desc, match_export); if (dev) { gpio_setup_irq(desc, dev, 0); clear_bit(FLAG_EXPORT, &desc->flags); } else status = -ENODEV; } mutex_unlock(&sysfs_lock); if (dev) { device_remove_file(dev, &dev_attr_edge); device_remove_file(dev, &dev_attr_direction); device_unregister(dev); put_device(dev); } if (status) gpiod_dbg(desc, "%s: status %d\n", __func__, status); } EXPORT_SYMBOL_GPL(gpiod_unexport); int gpiochip_export(struct gpio_chip *chip) { int status; struct device *dev; /* Many systems register gpio chips for SOC support very early, * before driver model support is available. In those cases we * export this later, in gpiolib_sysfs_init() ... here we just * verify that _some_ field of gpio_class got initialized. */ if (!gpio_class.p) return 0; /* use chip->base for the ID; it's already known to be unique */ mutex_lock(&sysfs_lock); dev = device_create_with_groups(&gpio_class, chip->dev, MKDEV(0, 0), chip, gpiochip_groups, "gpiochip%d", chip->base); if (IS_ERR(dev)) status = PTR_ERR(dev); else status = 0; chip->exported = (status == 0); mutex_unlock(&sysfs_lock); if (status) chip_dbg(chip, "%s: status %d\n", __func__, status); return status; } void gpiochip_unexport(struct gpio_chip *chip) { int status; struct device *dev; struct gpio_desc *desc; unsigned int i; mutex_lock(&sysfs_lock); dev = class_find_device(&gpio_class, NULL, chip, match_export); if (dev) { put_device(dev); device_unregister(dev); /* prevent further gpiod exports */ chip->exported = false; status = 0; } else status = -ENODEV; mutex_unlock(&sysfs_lock); if (status) chip_dbg(chip, "%s: status %d\n", __func__, status); /* unregister gpiod class devices owned by sysfs */ for (i = 0; i < chip->ngpio; i++) { desc = &chip->desc[i]; if (test_and_clear_bit(FLAG_SYSFS, &desc->flags)) gpiod_free(desc); } } static int __init gpiolib_sysfs_init(void) { int status; unsigned long flags; struct gpio_chip *chip; /*SWISTART*/ #ifdef CONFIG_SIERRA unsigned int gpio; #endif /*CONFIG_SIERRA*/ /*SWISTOP*/ status = class_register(&gpio_class); if (status < 0) return status; /*SWISTART*/ #ifdef CONFIG_SIERRA /* Assign product specific GPIO mapping */ gpio_set_map_table(); /* bsgetgpioflag retrieves modem configurable mask */ gpio_ext_chip.mask = bsgetgpioflag(); /* This loop does two things: * 1. Update the mask for any GPIO set to EMBEDDED_HOST in the * default table, which means that pin is EMBEDDED_HOST-only. * 2. Adds EMBEDDED_HOST function to any GPIO in the modem mask * * Note that GPIO set to EMBEDDED_HOST by default in the map tables * cannot be used for other functions. */ for (gpio = 0; gpio < gpio_ext_chip.ngpio; gpio++) { long ext_num; if (kstrtol(ext_gpio[gpio].gpio_name, 0, &ext_num) == 0) { if (ext_gpio[gpio].function == FUNCTION_EMBEDDED_HOST) gpio_ext_chip.mask |= (0x1ULL << (ext_num - 1)); else if (gpio_ext_chip.mask & (0x1ULL << (ext_num - 1))) ext_gpio[gpio].function = FUNCTION_EMBEDDED_HOST; else ext_gpio[gpio].function = FUNCTION_UNALLOCATED; } if (strcasecmp(ext_gpio[gpio].gpio_name, GPIO_NAME_RI) == 0) { gpio_ri = gpio; gpio_sync_ri(); break; } } getap_multiplex_gpio(); status = gpiochip_export(&gpio_ext_chip); /* we move sierra code away from gpio_lock here because the code * don't acquire a mutex or spin lock. * * __might_sleep() will dump out the error stack if sleeping function * is called from invaid context(spin_lock). e.g. bsreadhwconfig() */ #endif /*CONFIG_SIERRA*/ /*SWISTOP*/ /* Scan and register the gpio_chips which registered very * early (e.g. before the class_register above was called). * * We run before arch_initcall() so chip->dev nodes can have * registered, and so arch_initcall() can always gpio_export(). */ spin_lock_irqsave(&gpio_lock, flags); list_for_each_entry(chip, &gpio_chips, list) { if (chip->exported) continue; /* * TODO we yield gpio_lock here because gpiochip_export() * acquires a mutex. This is unsafe and needs to be fixed. * * Also it would be nice to use gpiochip_find() here so we * can keep gpio_chips local to gpiolib.c, but the yield of * gpio_lock prevents us from doing this. */ spin_unlock_irqrestore(&gpio_lock, flags); status = gpiochip_export(chip); spin_lock_irqsave(&gpio_lock, flags); } spin_unlock_irqrestore(&gpio_lock, flags); return status; } postcore_initcall(gpiolib_sysfs_init);