Skip to content

Commit

Permalink
Merge tag 'gpio-v4.6-3' of git://git.kernel.org/pub/scm/linux/kernel/…
Browse files Browse the repository at this point in the history
…git/linusw/linux-gpio

Pull GPIO fixes from Linus Walleij:
 "Here is a set of four GPIO fixes.  The two fixes to the core are
  serious as they are regressing minor architectures.

  Core fixes:

   - Defer GPIO device setup until after gpiolib is initialized.

     It turns out that a few very tightly integrated GPIO platform
     drivers initialize so early (befor core_initcall()) so that the
     gpiolib isn't even initialized itself.  That limits what the
     library can do, and we cannot reference uninitialized fields until
     later.

     Defer some of the initialization until right after the gpiolib is
     initialized in these (rare) cases.

   - As a consequence: do not use devm_* resources when allocating the
     states in the initial set-up of the gpiochip.

  Driver fixes:

   - In ACPI retrieveal: ignore GpioInt when looking for output GPIOs.

   - Fix legacy builds on the PXA without a backing pin controller.

   - Use correct datatype on pca953x register writes"

* tag 'gpio-v4.6-3' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio:
  gpio: pca953x: Use correct u16 value for register word write
  gpiolib: Defer gpio device setup until after gpiolib initialization
  gpiolib: Do not use devm functions when registering gpio chip
  gpio: pxa: fix legacy non pinctrl aware builds
  gpio / ACPI: ignore GpioInt() GPIOs when requesting GPIO_OUT_*
  • Loading branch information
torvalds committed Apr 9, 2016
2 parents 183c948 + 9b8e3ec commit 289b7bf
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 45 deletions.
3 changes: 2 additions & 1 deletion drivers/gpio/gpio-pca953x.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <linux/i2c.h>
#include <linux/platform_data/pca953x.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#include <linux/of_platform.h>
#include <linux/acpi.h>

Expand Down Expand Up @@ -159,7 +160,7 @@ static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val)
switch (chip->chip_type) {
case PCA953X_TYPE:
ret = i2c_smbus_write_word_data(chip->client,
reg << 1, (u16) *val);
reg << 1, cpu_to_le16(get_unaligned((u16 *)val)));
break;
case PCA957X_TYPE:
ret = i2c_smbus_write_byte_data(chip->client, reg << 1,
Expand Down
4 changes: 2 additions & 2 deletions drivers/gpio/gpio-pxa.c
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,8 @@ static int pxa_gpio_direction_output(struct gpio_chip *chip,
writel_relaxed(mask, base + (value ? GPSR_OFFSET : GPCR_OFFSET));

ret = pinctrl_gpio_direction_output(chip->base + offset);
if (!ret)
return 0;
if (ret)
return ret;

spin_lock_irqsave(&gpio_lock, flags);

Expand Down
133 changes: 91 additions & 42 deletions drivers/gpio/gpiolib.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ LIST_HEAD(gpio_devices);
static void gpiochip_free_hogs(struct gpio_chip *chip);
static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip);

static bool gpiolib_initialized;

static inline void desc_set_label(struct gpio_desc *d, const char *label)
{
Expand Down Expand Up @@ -440,9 +441,63 @@ static void gpiodevice_release(struct device *dev)
cdev_del(&gdev->chrdev);
list_del(&gdev->list);
ida_simple_remove(&gpio_ida, gdev->id);
kfree(gdev->label);
kfree(gdev->descs);
kfree(gdev);
}

static int gpiochip_setup_dev(struct gpio_device *gdev)
{
int status;

cdev_init(&gdev->chrdev, &gpio_fileops);
gdev->chrdev.owner = THIS_MODULE;
gdev->chrdev.kobj.parent = &gdev->dev.kobj;
gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id);
status = cdev_add(&gdev->chrdev, gdev->dev.devt, 1);
if (status < 0)
chip_warn(gdev->chip, "failed to add char device %d:%d\n",
MAJOR(gpio_devt), gdev->id);
else
chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n",
MAJOR(gpio_devt), gdev->id);
status = device_add(&gdev->dev);
if (status)
goto err_remove_chardev;

status = gpiochip_sysfs_register(gdev);
if (status)
goto err_remove_device;

/* From this point, the .release() function cleans up gpio_device */
gdev->dev.release = gpiodevice_release;
get_device(&gdev->dev);
pr_debug("%s: registered GPIOs %d to %d on device: %s (%s)\n",
__func__, gdev->base, gdev->base + gdev->ngpio - 1,
dev_name(&gdev->dev), gdev->chip->label ? : "generic");

return 0;

err_remove_device:
device_del(&gdev->dev);
err_remove_chardev:
cdev_del(&gdev->chrdev);
return status;
}

static void gpiochip_setup_devs(void)
{
struct gpio_device *gdev;
int err;

list_for_each_entry(gdev, &gpio_devices, list) {
err = gpiochip_setup_dev(gdev);
if (err)
pr_err("%s: Failed to initialize gpio device (%d)\n",
dev_name(&gdev->dev), err);
}
}

/**
* gpiochip_add_data() - register a gpio_chip
* @chip: the chip to register, with chip->base initialized
Expand All @@ -457,6 +512,9 @@ static void gpiodevice_release(struct device *dev)
* the gpio framework's arch_initcall(). Otherwise sysfs initialization
* for GPIOs will fail rudely.
*
* gpiochip_add_data() must only be called after gpiolib initialization,
* ie after core_initcall().
*
* If chip->base is negative, this requests dynamic assignment of
* a range of valid GPIOs.
*/
Expand Down Expand Up @@ -504,8 +562,7 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
else
gdev->owner = THIS_MODULE;

gdev->descs = devm_kcalloc(&gdev->dev, chip->ngpio,
sizeof(gdev->descs[0]), GFP_KERNEL);
gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);
if (!gdev->descs) {
status = -ENOMEM;
goto err_free_gdev;
Expand All @@ -514,16 +571,16 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
if (chip->ngpio == 0) {
chip_err(chip, "tried to insert a GPIO chip with zero lines\n");
status = -EINVAL;
goto err_free_gdev;
goto err_free_descs;
}

if (chip->label)
gdev->label = devm_kstrdup(&gdev->dev, chip->label, GFP_KERNEL);
gdev->label = kstrdup(chip->label, GFP_KERNEL);
else
gdev->label = devm_kstrdup(&gdev->dev, "unknown", GFP_KERNEL);
gdev->label = kstrdup("unknown", GFP_KERNEL);
if (!gdev->label) {
status = -ENOMEM;
goto err_free_gdev;
goto err_free_descs;
}

gdev->ngpio = chip->ngpio;
Expand All @@ -543,7 +600,7 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
if (base < 0) {
status = base;
spin_unlock_irqrestore(&gpio_lock, flags);
goto err_free_gdev;
goto err_free_label;
}
/*
* TODO: it should not be necessary to reflect the assigned
Expand All @@ -558,7 +615,7 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
status = gpiodev_add_to_list(gdev);
if (status) {
spin_unlock_irqrestore(&gpio_lock, flags);
goto err_free_gdev;
goto err_free_label;
}

for (i = 0; i < chip->ngpio; i++) {
Expand Down Expand Up @@ -596,39 +653,16 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
* we get a device node entry in sysfs under
* /sys/bus/gpio/devices/gpiochipN/dev that can be used for
* coldplug of device nodes and other udev business.
* We can do this only if gpiolib has been initialized.
* Otherwise, defer until later.
*/
cdev_init(&gdev->chrdev, &gpio_fileops);
gdev->chrdev.owner = THIS_MODULE;
gdev->chrdev.kobj.parent = &gdev->dev.kobj;
gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id);
status = cdev_add(&gdev->chrdev, gdev->dev.devt, 1);
if (status < 0)
chip_warn(chip, "failed to add char device %d:%d\n",
MAJOR(gpio_devt), gdev->id);
else
chip_dbg(chip, "added GPIO chardev (%d:%d)\n",
MAJOR(gpio_devt), gdev->id);
status = device_add(&gdev->dev);
if (status)
goto err_remove_chardev;

status = gpiochip_sysfs_register(gdev);
if (status)
goto err_remove_device;

/* From this point, the .release() function cleans up gpio_device */
gdev->dev.release = gpiodevice_release;
get_device(&gdev->dev);
pr_debug("%s: registered GPIOs %d to %d on device: %s (%s)\n",
__func__, gdev->base, gdev->base + gdev->ngpio - 1,
dev_name(&gdev->dev), chip->label ? : "generic");

if (gpiolib_initialized) {
status = gpiochip_setup_dev(gdev);
if (status)
goto err_remove_chip;
}
return 0;

err_remove_device:
device_del(&gdev->dev);
err_remove_chardev:
cdev_del(&gdev->chrdev);
err_remove_chip:
acpi_gpiochip_remove(chip);
gpiochip_free_hogs(chip);
Expand All @@ -637,6 +671,10 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data)
spin_lock_irqsave(&gpio_lock, flags);
list_del(&gdev->list);
spin_unlock_irqrestore(&gpio_lock, flags);
err_free_label:
kfree(gdev->label);
err_free_descs:
kfree(gdev->descs);
err_free_gdev:
ida_simple_remove(&gpio_ida, gdev->id);
/* failures here can mean systems won't boot... */
Expand Down Expand Up @@ -2231,9 +2269,11 @@ static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
return desc;
}

static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id,
static struct gpio_desc *acpi_find_gpio(struct device *dev,
const char *con_id,
unsigned int idx,
enum gpio_lookup_flags *flags)
enum gpiod_flags flags,
enum gpio_lookup_flags *lookupflags)
{
struct acpi_device *adev = ACPI_COMPANION(dev);
struct acpi_gpio_info info;
Expand Down Expand Up @@ -2264,10 +2304,16 @@ static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id,
desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info);
if (IS_ERR(desc))
return desc;

if ((flags == GPIOD_OUT_LOW || flags == GPIOD_OUT_HIGH) &&
info.gpioint) {
dev_dbg(dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n");
return ERR_PTR(-ENOENT);
}
}

if (info.polarity == GPIO_ACTIVE_LOW)
*flags |= GPIO_ACTIVE_LOW;
*lookupflags |= GPIO_ACTIVE_LOW;

return desc;
}
Expand Down Expand Up @@ -2530,7 +2576,7 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
desc = of_find_gpio(dev, con_id, idx, &lookupflags);
} else if (ACPI_COMPANION(dev)) {
dev_dbg(dev, "using ACPI for GPIO lookup\n");
desc = acpi_find_gpio(dev, con_id, idx, &lookupflags);
desc = acpi_find_gpio(dev, con_id, idx, flags, &lookupflags);
}
}

Expand Down Expand Up @@ -2829,6 +2875,9 @@ static int __init gpiolib_dev_init(void)
if (ret < 0) {
pr_err("gpiolib: failed to allocate char dev region\n");
bus_unregister(&gpio_bus_type);
} else {
gpiolib_initialized = true;
gpiochip_setup_devs();
}
return ret;
}
Expand Down

0 comments on commit 289b7bf

Please sign in to comment.