diff options
Diffstat (limited to 'drivers/gpio/gpio-mpc8xxx.c')
| -rw-r--r-- | drivers/gpio/gpio-mpc8xxx.c | 305 |
1 files changed, 205 insertions, 100 deletions
diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 793518a30afe..bfe828734ee1 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -1,26 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * GPIOs on MPC512x/8349/8572/8610/QorIQ and compatible * * Copyright (C) 2008 Peter Korsgaard <jacmet@sunsite.dk> * Copyright (C) 2016 Freescale Semiconductor Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ -#include <linux/kernel.h> +#include <linux/acpi.h> +#include <linux/bitops.h> +#include <linux/gpio/driver.h> +#include <linux/gpio/generic.h> #include <linux/init.h> -#include <linux/spinlock.h> +#include <linux/interrupt.h> #include <linux/io.h> -#include <linux/of.h> -#include <linux/of_gpio.h> -#include <linux/of_address.h> -#include <linux/of_irq.h> -#include <linux/of_platform.h> -#include <linux/slab.h> #include <linux/irq.h> -#include <linux/gpio/driver.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/slab.h> +#include <linux/spinlock.h> #define MPC8XXX_GPIO_PINS 32 @@ -31,9 +32,10 @@ #define GPIO_IMR 0x10 #define GPIO_ICR 0x14 #define GPIO_ICR2 0x18 +#define GPIO_IBE 0x18 struct mpc8xxx_gpio_chip { - struct gpio_chip gc; + struct gpio_generic_chip chip; void __iomem *regs; raw_spinlock_t lock; @@ -41,9 +43,19 @@ struct mpc8xxx_gpio_chip { unsigned offset, int value); struct irq_domain *irq; - unsigned int irqn; + int irqn; }; +/* + * This hardware has a big endian bit assignment such that GPIO line 0 is + * connected to bit 31, line 1 to bit 30 ... line 31 to bit 0. + * This inline helper give the right bitmask for a certain line. + */ +static inline u32 mpc_pin2mask(unsigned int offset) +{ + return BIT(31 - offset); +} + /* Workaround GPIO 1 errata on MPC8572/MPC8536. The status of GPIOs * defined as output cannot be determined by reading GPDAT register, * so we use shadow data register instead. The status of input pins @@ -55,11 +67,13 @@ static int mpc8572_gpio_get(struct gpio_chip *gc, unsigned int gpio) struct mpc8xxx_gpio_chip *mpc8xxx_gc = gpiochip_get_data(gc); u32 out_mask, out_shadow; - out_mask = gc->read_reg(mpc8xxx_gc->regs + GPIO_DIR); - val = gc->read_reg(mpc8xxx_gc->regs + GPIO_DAT) & ~out_mask; - out_shadow = gc->bgpio_data & out_mask; + out_mask = gpio_generic_read_reg(&mpc8xxx_gc->chip, + mpc8xxx_gc->regs + GPIO_DIR); + val = gpio_generic_read_reg(&mpc8xxx_gc->chip, + mpc8xxx_gc->regs + GPIO_DAT) & ~out_mask; + out_shadow = mpc8xxx_gc->chip.sdata & out_mask; - return !!((val | out_shadow) & gc->pin2mask(gc, gpio)); + return !!((val | out_shadow) & mpc_pin2mask(gpio)); } static int mpc5121_gpio_dir_out(struct gpio_chip *gc, @@ -94,33 +108,38 @@ static int mpc8xxx_gpio_to_irq(struct gpio_chip *gc, unsigned offset) return -ENXIO; } -static void mpc8xxx_gpio_irq_cascade(struct irq_desc *desc) +static irqreturn_t mpc8xxx_gpio_irq_cascade(int irq, void *data) { - struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_desc_get_handler_data(desc); - struct irq_chip *chip = irq_desc_get_chip(desc); - struct gpio_chip *gc = &mpc8xxx_gc->gc; - unsigned int mask; - - mask = gc->read_reg(mpc8xxx_gc->regs + GPIO_IER) - & gc->read_reg(mpc8xxx_gc->regs + GPIO_IMR); - if (mask) - generic_handle_irq(irq_linear_revmap(mpc8xxx_gc->irq, - 32 - ffs(mask))); - if (chip->irq_eoi) - chip->irq_eoi(&desc->irq_data); + struct mpc8xxx_gpio_chip *mpc8xxx_gc = data; + unsigned long mask; + int i; + + mask = gpio_generic_read_reg(&mpc8xxx_gc->chip, + mpc8xxx_gc->regs + GPIO_IER) & + gpio_generic_read_reg(&mpc8xxx_gc->chip, + mpc8xxx_gc->regs + GPIO_IMR); + for_each_set_bit(i, &mask, 32) + generic_handle_domain_irq(mpc8xxx_gc->irq, 31 - i); + + return IRQ_HANDLED; } static void mpc8xxx_irq_unmask(struct irq_data *d) { struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); - struct gpio_chip *gc = &mpc8xxx_gc->gc; + irq_hw_number_t hwirq = irqd_to_hwirq(d); + struct gpio_chip *gc = &mpc8xxx_gc->chip.gc; unsigned long flags; + gpiochip_enable_irq(gc, hwirq); + raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - gc->write_reg(mpc8xxx_gc->regs + GPIO_IMR, - gc->read_reg(mpc8xxx_gc->regs + GPIO_IMR) - | gc->pin2mask(gc, irqd_to_hwirq(d))); + gpio_generic_write_reg(&mpc8xxx_gc->chip, + mpc8xxx_gc->regs + GPIO_IMR, + gpio_generic_read_reg(&mpc8xxx_gc->chip, + mpc8xxx_gc->regs + GPIO_IMR) + | mpc_pin2mask(irqd_to_hwirq(d))); raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); } @@ -128,47 +147,54 @@ static void mpc8xxx_irq_unmask(struct irq_data *d) static void mpc8xxx_irq_mask(struct irq_data *d) { struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); - struct gpio_chip *gc = &mpc8xxx_gc->gc; + irq_hw_number_t hwirq = irqd_to_hwirq(d); + struct gpio_chip *gc = &mpc8xxx_gc->chip.gc; unsigned long flags; raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - gc->write_reg(mpc8xxx_gc->regs + GPIO_IMR, - gc->read_reg(mpc8xxx_gc->regs + GPIO_IMR) - & ~(gc->pin2mask(gc, irqd_to_hwirq(d)))); + gpio_generic_write_reg(&mpc8xxx_gc->chip, mpc8xxx_gc->regs + GPIO_IMR, + gpio_generic_read_reg(&mpc8xxx_gc->chip, + mpc8xxx_gc->regs + GPIO_IMR) + & ~mpc_pin2mask(irqd_to_hwirq(d))); raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); + + gpiochip_disable_irq(gc, hwirq); } static void mpc8xxx_irq_ack(struct irq_data *d) { struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); - struct gpio_chip *gc = &mpc8xxx_gc->gc; - gc->write_reg(mpc8xxx_gc->regs + GPIO_IER, - gc->pin2mask(gc, irqd_to_hwirq(d))); + gpio_generic_write_reg(&mpc8xxx_gc->chip, mpc8xxx_gc->regs + GPIO_IER, + mpc_pin2mask(irqd_to_hwirq(d))); } static int mpc8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type) { struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); - struct gpio_chip *gc = &mpc8xxx_gc->gc; unsigned long flags; switch (flow_type) { case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_LEVEL_LOW: raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - gc->write_reg(mpc8xxx_gc->regs + GPIO_ICR, - gc->read_reg(mpc8xxx_gc->regs + GPIO_ICR) - | gc->pin2mask(gc, irqd_to_hwirq(d))); + gpio_generic_write_reg(&mpc8xxx_gc->chip, + mpc8xxx_gc->regs + GPIO_ICR, + gpio_generic_read_reg(&mpc8xxx_gc->chip, + mpc8xxx_gc->regs + GPIO_ICR) + | mpc_pin2mask(irqd_to_hwirq(d))); raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); break; case IRQ_TYPE_EDGE_BOTH: raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - gc->write_reg(mpc8xxx_gc->regs + GPIO_ICR, - gc->read_reg(mpc8xxx_gc->regs + GPIO_ICR) - & ~(gc->pin2mask(gc, irqd_to_hwirq(d)))); + gpio_generic_write_reg(&mpc8xxx_gc->chip, + mpc8xxx_gc->regs + GPIO_ICR, + gpio_generic_read_reg(&mpc8xxx_gc->chip, + mpc8xxx_gc->regs + GPIO_ICR) + & ~mpc_pin2mask(irqd_to_hwirq(d))); raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); break; @@ -182,7 +208,6 @@ static int mpc8xxx_irq_set_type(struct irq_data *d, unsigned int flow_type) static int mpc512x_irq_set_type(struct irq_data *d, unsigned int flow_type) { struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); - struct gpio_chip *gc = &mpc8xxx_gc->gc; unsigned long gpio = irqd_to_hwirq(d); void __iomem *reg; unsigned int shift; @@ -200,7 +225,9 @@ static int mpc512x_irq_set_type(struct irq_data *d, unsigned int flow_type) case IRQ_TYPE_EDGE_FALLING: case IRQ_TYPE_LEVEL_LOW: raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - gc->write_reg(reg, (gc->read_reg(reg) & ~(3 << shift)) + gpio_generic_write_reg(&mpc8xxx_gc->chip, reg, + (gpio_generic_read_reg(&mpc8xxx_gc->chip, + reg) & ~(3 << shift)) | (2 << shift)); raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); break; @@ -208,14 +235,18 @@ static int mpc512x_irq_set_type(struct irq_data *d, unsigned int flow_type) case IRQ_TYPE_EDGE_RISING: case IRQ_TYPE_LEVEL_HIGH: raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - gc->write_reg(reg, (gc->read_reg(reg) & ~(3 << shift)) + gpio_generic_write_reg(&mpc8xxx_gc->chip, reg, + (gpio_generic_read_reg(&mpc8xxx_gc->chip, + reg) & ~(3 << shift)) | (1 << shift)); raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); break; case IRQ_TYPE_EDGE_BOTH: raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags); - gc->write_reg(reg, (gc->read_reg(reg) & ~(3 << shift))); + gpio_generic_write_reg(&mpc8xxx_gc->chip, reg, + (gpio_generic_read_reg(&mpc8xxx_gc->chip, + reg) & ~(3 << shift))); raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); break; @@ -233,6 +264,8 @@ static struct irq_chip mpc8xxx_irq_chip = { .irq_ack = mpc8xxx_irq_ack, /* this might get overwritten in mpc8xxx_probe() */ .irq_set_type = mpc8xxx_irq_set_type, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static int mpc8xxx_gpio_irq_map(struct irq_domain *h, unsigned int irq, @@ -274,26 +307,30 @@ static const struct mpc8xxx_gpio_devtype mpc8xxx_gpio_devtype_default = { }; static const struct of_device_id mpc8xxx_gpio_ids[] = { + { .compatible = "fsl,mpc8314-gpio", }, { .compatible = "fsl,mpc8349-gpio", }, { .compatible = "fsl,mpc8572-gpio", .data = &mpc8572_gpio_devtype, }, { .compatible = "fsl,mpc8610-gpio", }, { .compatible = "fsl,mpc5121-gpio", .data = &mpc512x_gpio_devtype, }, { .compatible = "fsl,mpc5125-gpio", .data = &mpc5125_gpio_devtype, }, { .compatible = "fsl,pq3-gpio", }, + { .compatible = "fsl,ls1028a-gpio", }, + { .compatible = "fsl,ls1088a-gpio", }, { .compatible = "fsl,qoriq-gpio", }, {} }; static int mpc8xxx_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; + const struct mpc8xxx_gpio_devtype *devtype = NULL; + struct gpio_generic_chip_config config; struct mpc8xxx_gpio_chip *mpc8xxx_gc; - struct gpio_chip *gc; - const struct mpc8xxx_gpio_devtype *devtype = - of_device_get_match_data(&pdev->dev); + struct device *dev = &pdev->dev; + struct fwnode_handle *fwnode; + struct gpio_chip *gc; int ret; - mpc8xxx_gc = devm_kzalloc(&pdev->dev, sizeof(*mpc8xxx_gc), GFP_KERNEL); + mpc8xxx_gc = devm_kzalloc(dev, sizeof(*mpc8xxx_gc), GFP_KERNEL); if (!mpc8xxx_gc) return -ENOMEM; @@ -301,35 +338,35 @@ static int mpc8xxx_probe(struct platform_device *pdev) raw_spin_lock_init(&mpc8xxx_gc->lock); - mpc8xxx_gc->regs = of_iomap(np, 0); - if (!mpc8xxx_gc->regs) - return -ENOMEM; + mpc8xxx_gc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mpc8xxx_gc->regs)) + return PTR_ERR(mpc8xxx_gc->regs); + + gc = &mpc8xxx_gc->chip.gc; + gc->parent = dev; + + config = (struct gpio_generic_chip_config) { + .dev = dev, + .sz = 4, + .dat = mpc8xxx_gc->regs + GPIO_DAT, + .dirout = mpc8xxx_gc->regs + GPIO_DIR, + .flags = GPIO_GENERIC_BIG_ENDIAN + }; - gc = &mpc8xxx_gc->gc; - - if (of_property_read_bool(np, "little-endian")) { - ret = bgpio_init(gc, &pdev->dev, 4, - mpc8xxx_gc->regs + GPIO_DAT, - NULL, NULL, - mpc8xxx_gc->regs + GPIO_DIR, NULL, - BGPIOF_BIG_ENDIAN); - if (ret) - goto err; - dev_dbg(&pdev->dev, "GPIO registers are LITTLE endian\n"); + if (device_property_read_bool(dev, "little-endian")) { + dev_dbg(dev, "GPIO registers are LITTLE endian\n"); } else { - ret = bgpio_init(gc, &pdev->dev, 4, - mpc8xxx_gc->regs + GPIO_DAT, - NULL, NULL, - mpc8xxx_gc->regs + GPIO_DIR, NULL, - BGPIOF_BIG_ENDIAN - | BGPIOF_BIG_ENDIAN_BYTE_ORDER); - if (ret) - goto err; - dev_dbg(&pdev->dev, "GPIO registers are BIG endian\n"); + config.flags |= GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER; + dev_dbg(dev, "GPIO registers are BIG endian\n"); } + ret = gpio_generic_chip_init(&mpc8xxx_gc->chip, &config); + if (ret) + return ret; + mpc8xxx_gc->direction_output = gc->direction_output; + devtype = device_get_match_data(dev); if (!devtype) devtype = &mpc8xxx_gpio_devtype_default; @@ -337,7 +374,8 @@ static int mpc8xxx_probe(struct platform_device *pdev) * It's assumed that only a single type of gpio controller is available * on the current machine, so overwriting global data is fine. */ - mpc8xxx_irq_chip.irq_set_type = devtype->irq_set_type; + if (devtype->irq_set_type) + mpc8xxx_irq_chip.irq_set_type = devtype->irq_set_type; if (devtype->gpio_dir_out) gc->direction_output = devtype->gpio_dir_out; @@ -346,35 +384,74 @@ static int mpc8xxx_probe(struct platform_device *pdev) gc->to_irq = mpc8xxx_gpio_to_irq; - ret = gpiochip_add_data(gc, mpc8xxx_gc); + /* + * The GPIO Input Buffer Enable register(GPIO_IBE) is used to control + * the input enable of each individual GPIO port. When an individual + * GPIO port’s direction is set to input (GPIO_GPDIR[DRn=0]), the + * associated input enable must be set (GPIOxGPIE[IEn]=1) to propagate + * the port value to the GPIO Data Register. + */ + fwnode = dev_fwnode(dev); + if (device_is_compatible(dev, "fsl,qoriq-gpio") || + device_is_compatible(dev, "fsl,ls1028a-gpio") || + device_is_compatible(dev, "fsl,ls1088a-gpio") || + is_acpi_node(fwnode)) { + gpio_generic_write_reg(&mpc8xxx_gc->chip, + mpc8xxx_gc->regs + GPIO_IBE, 0xffffffff); + /* Also, latch state of GPIOs configured as output by bootloader. */ + mpc8xxx_gc->chip.sdata = + gpio_generic_read_reg(&mpc8xxx_gc->chip, + mpc8xxx_gc->regs + GPIO_DAT) & + gpio_generic_read_reg(&mpc8xxx_gc->chip, + mpc8xxx_gc->regs + GPIO_DIR); + } + + ret = devm_gpiochip_add_data(dev, gc, mpc8xxx_gc); if (ret) { - pr_err("%s: GPIO chip registration failed with status %d\n", - np->full_name, ret); - goto err; + dev_err(dev, + "GPIO chip registration failed with status %d\n", ret); + return ret; } - mpc8xxx_gc->irqn = irq_of_parse_and_map(np, 0); - if (!mpc8xxx_gc->irqn) - return 0; + mpc8xxx_gc->irqn = platform_get_irq(pdev, 0); + if (mpc8xxx_gc->irqn < 0) + return mpc8xxx_gc->irqn; + + mpc8xxx_gc->irq = irq_domain_create_linear(fwnode, + MPC8XXX_GPIO_PINS, + &mpc8xxx_gpio_irq_ops, + mpc8xxx_gc); - mpc8xxx_gc->irq = irq_domain_add_linear(np, MPC8XXX_GPIO_PINS, - &mpc8xxx_gpio_irq_ops, mpc8xxx_gc); if (!mpc8xxx_gc->irq) return 0; /* ack and mask all irqs */ - gc->write_reg(mpc8xxx_gc->regs + GPIO_IER, 0xffffffff); - gc->write_reg(mpc8xxx_gc->regs + GPIO_IMR, 0); + gpio_generic_write_reg(&mpc8xxx_gc->chip, + mpc8xxx_gc->regs + GPIO_IER, 0xffffffff); + gpio_generic_write_reg(&mpc8xxx_gc->chip, + mpc8xxx_gc->regs + GPIO_IMR, 0); + + ret = devm_request_irq(dev, mpc8xxx_gc->irqn, + mpc8xxx_gpio_irq_cascade, + IRQF_NO_THREAD | IRQF_SHARED, "gpio-cascade", + mpc8xxx_gc); + if (ret) { + dev_err(dev, "failed to devm_request_irq(%d), ret = %d\n", + mpc8xxx_gc->irqn, ret); + goto err; + } + + ret = devm_device_init_wakeup(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to init wakeup\n"); - irq_set_chained_handler_and_data(mpc8xxx_gc->irqn, - mpc8xxx_gpio_irq_cascade, mpc8xxx_gc); return 0; err: - iounmap(mpc8xxx_gc->regs); + irq_domain_remove(mpc8xxx_gc->irq); return ret; } -static int mpc8xxx_remove(struct platform_device *pdev) +static void mpc8xxx_remove(struct platform_device *pdev) { struct mpc8xxx_gpio_chip *mpc8xxx_gc = platform_get_drvdata(pdev); @@ -382,19 +459,47 @@ static int mpc8xxx_remove(struct platform_device *pdev) irq_set_chained_handler_and_data(mpc8xxx_gc->irqn, NULL, NULL); irq_domain_remove(mpc8xxx_gc->irq); } +} - gpiochip_remove(&mpc8xxx_gc->gc); - iounmap(mpc8xxx_gc->regs); +static int mpc8xxx_suspend(struct device *dev) +{ + struct mpc8xxx_gpio_chip *mpc8xxx_gc = dev_get_drvdata(dev); + + if (mpc8xxx_gc->irqn && device_may_wakeup(dev)) + enable_irq_wake(mpc8xxx_gc->irqn); return 0; } +static int mpc8xxx_resume(struct device *dev) +{ + struct mpc8xxx_gpio_chip *mpc8xxx_gc = dev_get_drvdata(dev); + + if (mpc8xxx_gc->irqn && device_may_wakeup(dev)) + disable_irq_wake(mpc8xxx_gc->irqn); + + return 0; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(mpc8xx_pm_ops, + mpc8xxx_suspend, mpc8xxx_resume, NULL); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id gpio_acpi_ids[] = { + {"NXP0031",}, + { } +}; +MODULE_DEVICE_TABLE(acpi, gpio_acpi_ids); +#endif + static struct platform_driver mpc8xxx_plat_driver = { .probe = mpc8xxx_probe, .remove = mpc8xxx_remove, .driver = { .name = "gpio-mpc8xxx", .of_match_table = mpc8xxx_gpio_ids, + .acpi_match_table = ACPI_PTR(gpio_acpi_ids), + .pm = pm_ptr(&mpc8xx_pm_ops), }, }; |
