summaryrefslogtreecommitdiffstats
path: root/drivers/gpio/gpio-omap.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/gpio-omap.c')
-rw-r--r--drivers/gpio/gpio-omap.c291
1 files changed, 211 insertions, 80 deletions
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index f49bd6f..1adc2ec 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -19,9 +19,12 @@
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
-#include <linux/slab.h>
+#include <linux/device.h>
#include <linux/pm_runtime.h>
#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/irqdomain.h>
#include <mach/hardware.h>
#include <asm/irq.h>
@@ -50,10 +53,10 @@ struct gpio_regs {
struct gpio_bank {
struct list_head node;
- unsigned long pbase;
void __iomem *base;
u16 irq;
- u16 virtual_irq_start;
+ int irq_base;
+ struct irq_domain *domain;
u32 suspend_wakeup;
u32 saved_wakeup;
u32 non_wakeup_gpios;
@@ -77,7 +80,6 @@ struct gpio_bank {
int stride;
u32 width;
int context_loss_count;
- u16 id;
int power_mode;
bool workaround_enabled;
@@ -91,6 +93,11 @@ struct gpio_bank {
#define GPIO_BIT(bank, gpio) (1 << GPIO_INDEX(bank, gpio))
#define GPIO_MOD_CTRL_BIT BIT(0)
+static int irq_to_gpio(struct gpio_bank *bank, unsigned int gpio_irq)
+{
+ return gpio_irq - bank->irq_base + bank->chip.base;
+}
+
static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input)
{
void __iomem *reg = bank->base;
@@ -113,10 +120,13 @@ static void _set_gpio_dataout_reg(struct gpio_bank *bank, int gpio, int enable)
void __iomem *reg = bank->base;
u32 l = GPIO_BIT(bank, gpio);
- if (enable)
+ if (enable) {
reg += bank->regs->set_dataout;
- else
+ bank->context.dataout |= l;
+ } else {
reg += bank->regs->clr_dataout;
+ bank->context.dataout &= ~l;
+ }
__raw_writel(l, reg);
}
@@ -137,25 +147,25 @@ static void _set_gpio_dataout_mask(struct gpio_bank *bank, int gpio, int enable)
bank->context.dataout = l;
}
-static int _get_gpio_datain(struct gpio_bank *bank, int gpio)
+static int _get_gpio_datain(struct gpio_bank *bank, int offset)
{
void __iomem *reg = bank->base + bank->regs->datain;
- return (__raw_readl(reg) & GPIO_BIT(bank, gpio)) != 0;
+ return (__raw_readl(reg) & (1 << offset)) != 0;
}
-static int _get_gpio_dataout(struct gpio_bank *bank, int gpio)
+static int _get_gpio_dataout(struct gpio_bank *bank, int offset)
{
void __iomem *reg = bank->base + bank->regs->dataout;
- return (__raw_readl(reg) & GPIO_BIT(bank, gpio)) != 0;
+ return (__raw_readl(reg) & (1 << offset)) != 0;
}
static inline void _gpio_rmw(void __iomem *base, u32 reg, u32 mask, bool set)
{
int l = __raw_readl(base + reg);
- if (set)
+ if (set)
l |= mask;
else
l &= ~mask;
@@ -238,7 +248,7 @@ static void _set_gpio_debounce(struct gpio_bank *bank, unsigned gpio,
}
static inline void set_gpio_trigger(struct gpio_bank *bank, int gpio,
- int trigger)
+ unsigned trigger)
{
void __iomem *base = bank->base;
u32 gpio_bit = 1 << gpio;
@@ -320,7 +330,8 @@ static void _toggle_gpio_edge_triggering(struct gpio_bank *bank, int gpio)
static void _toggle_gpio_edge_triggering(struct gpio_bank *bank, int gpio) {}
#endif
-static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger)
+static int _set_gpio_triggering(struct gpio_bank *bank, int gpio,
+ unsigned trigger)
{
void __iomem *reg = bank->base;
void __iomem *base = bank->base;
@@ -367,7 +378,7 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger)
static int gpio_irq_type(struct irq_data *d, unsigned type)
{
- struct gpio_bank *bank;
+ struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
unsigned gpio;
int retval;
unsigned long flags;
@@ -375,13 +386,11 @@ static int gpio_irq_type(struct irq_data *d, unsigned type)
if (!cpu_class_is_omap2() && d->irq > IH_MPUIO_BASE)
gpio = OMAP_MPUIO(d->irq - IH_MPUIO_BASE);
else
- gpio = d->irq - IH_GPIO_BASE;
+ gpio = irq_to_gpio(bank, d->irq);
if (type & ~IRQ_TYPE_SENSE_MASK)
return -EINVAL;
- bank = irq_data_get_irq_chip_data(d);
-
if (!bank->regs->leveldetect0 &&
(type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH)))
return -EINVAL;
@@ -442,6 +451,7 @@ static void _enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
if (bank->regs->set_irqenable) {
reg += bank->regs->set_irqenable;
l = gpio_mask;
+ bank->context.irqenable1 |= gpio_mask;
} else {
reg += bank->regs->irqenable;
l = __raw_readl(reg);
@@ -449,10 +459,10 @@ static void _enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
l &= ~gpio_mask;
else
l |= gpio_mask;
+ bank->context.irqenable1 = l;
}
__raw_writel(l, reg);
- bank->context.irqenable1 = l;
}
static void _disable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
@@ -463,6 +473,7 @@ static void _disable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
if (bank->regs->clr_irqenable) {
reg += bank->regs->clr_irqenable;
l = gpio_mask;
+ bank->context.irqenable1 &= ~gpio_mask;
} else {
reg += bank->regs->irqenable;
l = __raw_readl(reg);
@@ -470,15 +481,18 @@ static void _disable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
l |= gpio_mask;
else
l &= ~gpio_mask;
+ bank->context.irqenable1 = l;
}
__raw_writel(l, reg);
- bank->context.irqenable1 = l;
}
static inline void _set_gpio_irqenable(struct gpio_bank *bank, int gpio, int enable)
{
- _enable_gpio_irqbank(bank, GPIO_BIT(bank, gpio));
+ if (enable)
+ _enable_gpio_irqbank(bank, GPIO_BIT(bank, gpio));
+ else
+ _disable_gpio_irqbank(bank, GPIO_BIT(bank, gpio));
}
/*
@@ -495,7 +509,7 @@ static int _set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable)
unsigned long flags;
if (bank->non_wakeup_gpios & gpio_bit) {
- dev_err(bank->dev,
+ dev_err(bank->dev,
"Unable to modify wakeup on non-wakeup GPIO%d\n", gpio);
return -EINVAL;
}
@@ -506,6 +520,7 @@ static int _set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable)
else
bank->suspend_wakeup &= ~gpio_bit;
+ __raw_writel(bank->suspend_wakeup, bank->base + bank->regs->wkup_en);
spin_unlock_irqrestore(&bank->lock, flags);
return 0;
@@ -522,14 +537,10 @@ static void _reset_gpio(struct gpio_bank *bank, int gpio)
/* Use disable_irq_wake() and enable_irq_wake() functions from drivers */
static int gpio_wake_enable(struct irq_data *d, unsigned int enable)
{
- unsigned int gpio = d->irq - IH_GPIO_BASE;
- struct gpio_bank *bank;
- int retval;
-
- bank = irq_data_get_irq_chip_data(d);
- retval = _set_gpio_wakeup(bank, gpio, enable);
+ struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
+ unsigned int gpio = irq_to_gpio(bank, d->irq);
- return retval;
+ return _set_gpio_wakeup(bank, gpio, enable);
}
static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
@@ -671,13 +682,15 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
if (!isr)
break;
- gpio_irq = bank->virtual_irq_start;
+ gpio_irq = bank->irq_base;
for (; isr != 0; isr >>= 1, gpio_irq++) {
- gpio_index = GPIO_INDEX(bank, irq_to_gpio(gpio_irq));
+ int gpio = irq_to_gpio(bank, gpio_irq);
if (!(isr & 1))
continue;
+ gpio_index = GPIO_INDEX(bank, gpio);
+
/*
* Some chips can't respond to both rising and falling
* at the same time. If this irq was requested with
@@ -703,8 +716,8 @@ exit:
static void gpio_irq_shutdown(struct irq_data *d)
{
- unsigned int gpio = d->irq - IH_GPIO_BASE;
struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
+ unsigned int gpio = irq_to_gpio(bank, d->irq);
unsigned long flags;
spin_lock_irqsave(&bank->lock, flags);
@@ -714,16 +727,16 @@ static void gpio_irq_shutdown(struct irq_data *d)
static void gpio_ack_irq(struct irq_data *d)
{
- unsigned int gpio = d->irq - IH_GPIO_BASE;
struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
+ unsigned int gpio = irq_to_gpio(bank, d->irq);
_clear_gpio_irqstatus(bank, gpio);
}
static void gpio_mask_irq(struct irq_data *d)
{
- unsigned int gpio = d->irq - IH_GPIO_BASE;
struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
+ unsigned int gpio = irq_to_gpio(bank, d->irq);
unsigned long flags;
spin_lock_irqsave(&bank->lock, flags);
@@ -734,8 +747,8 @@ static void gpio_mask_irq(struct irq_data *d)
static void gpio_unmask_irq(struct irq_data *d)
{
- unsigned int gpio = d->irq - IH_GPIO_BASE;
struct gpio_bank *bank = irq_data_get_irq_chip_data(d);
+ unsigned int gpio = irq_to_gpio(bank, d->irq);
unsigned int irq_mask = GPIO_BIT(bank, gpio);
u32 trigger = irqd_get_trigger_type(d);
unsigned long flags;
@@ -852,19 +865,15 @@ static int gpio_is_input(struct gpio_bank *bank, int mask)
static int gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct gpio_bank *bank;
- void __iomem *reg;
- int gpio;
u32 mask;
- gpio = chip->base + offset;
bank = container_of(chip, struct gpio_bank, chip);
- reg = bank->base;
- mask = GPIO_BIT(bank, gpio);
+ mask = (1 << offset);
if (gpio_is_input(bank, mask))
- return _get_gpio_datain(bank, gpio);
+ return _get_gpio_datain(bank, offset);
else
- return _get_gpio_dataout(bank, gpio);
+ return _get_gpio_dataout(bank, offset);
}
static int gpio_output(struct gpio_chip *chip, unsigned offset, int value)
@@ -917,7 +926,7 @@ static int gpio_2irq(struct gpio_chip *chip, unsigned offset)
struct gpio_bank *bank;
bank = container_of(chip, struct gpio_bank, chip);
- return bank->virtual_irq_start + offset;
+ return bank->irq_base + offset;
}
/*---------------------------------------------------------------------*/
@@ -970,7 +979,7 @@ static void omap_gpio_mod_init(struct gpio_bank *bank)
_gpio_rmw(base, bank->regs->ctrl, 0, 1);
}
-static __init void
+static __devinit void
omap_mpuio_alloc_gc(struct gpio_bank *bank, unsigned int irq_start,
unsigned int num)
{
@@ -1030,8 +1039,7 @@ static void __devinit omap_gpio_chip_init(struct gpio_bank *bank)
gpiochip_add(&bank->chip);
- for (j = bank->virtual_irq_start;
- j < bank->virtual_irq_start + bank->width; j++) {
+ for (j = bank->irq_base; j < bank->irq_base + bank->width; j++) {
irq_set_lockdep_class(j, &gpio_lock_class);
irq_set_chip_data(j, bank);
if (bank->is_mpuio) {
@@ -1046,39 +1054,38 @@ static void __devinit omap_gpio_chip_init(struct gpio_bank *bank)
irq_set_handler_data(bank->irq, bank);
}
+static const struct of_device_id omap_gpio_match[];
+
static int __devinit omap_gpio_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ const struct of_device_id *match;
struct omap_gpio_platform_data *pdata;
struct resource *res;
struct gpio_bank *bank;
int ret = 0;
- if (!pdev->dev.platform_data) {
- ret = -EINVAL;
- goto err_exit;
- }
+ match = of_match_device(of_match_ptr(omap_gpio_match), dev);
+
+ pdata = match ? match->data : dev->platform_data;
+ if (!pdata)
+ return -EINVAL;
- bank = kzalloc(sizeof(struct gpio_bank), GFP_KERNEL);
+ bank = devm_kzalloc(&pdev->dev, sizeof(struct gpio_bank), GFP_KERNEL);
if (!bank) {
- dev_err(&pdev->dev, "Memory alloc failed for gpio_bank\n");
- ret = -ENOMEM;
- goto err_exit;
+ dev_err(dev, "Memory alloc failed\n");
+ return -ENOMEM;
}
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (unlikely(!res)) {
- dev_err(&pdev->dev, "GPIO Bank %i Invalid IRQ resource\n",
- pdev->id);
- ret = -ENODEV;
- goto err_free;
+ dev_err(dev, "Invalid IRQ resource\n");
+ return -ENODEV;
}
bank->irq = res->start;
- bank->id = pdev->id;
-
- pdata = pdev->dev.platform_data;
- bank->virtual_irq_start = pdata->virtual_irq_start;
- bank->dev = &pdev->dev;
+ bank->dev = dev;
bank->dbck_flag = pdata->dbck_flag;
bank->stride = pdata->bank_stride;
bank->width = pdata->bank_width;
@@ -1087,6 +1094,18 @@ static int __devinit omap_gpio_probe(struct platform_device *pdev)
bank->loses_context = pdata->loses_context;
bank->get_context_loss_count = pdata->get_context_loss_count;
bank->regs = pdata->regs;
+#ifdef CONFIG_OF_GPIO
+ bank->chip.of_node = of_node_get(node);
+#endif
+
+ bank->irq_base = irq_alloc_descs(-1, 0, bank->width, 0);
+ if (bank->irq_base < 0) {
+ dev_err(dev, "Couldn't allocate IRQ numbers\n");
+ return -ENODEV;
+ }
+
+ bank->domain = irq_domain_add_legacy(node, bank->width, bank->irq_base,
+ 0, &irq_domain_simple_ops, NULL);
if (bank->regs->set_dataout && bank->regs->clr_dataout)
bank->set_dataout = _set_gpio_dataout_reg;
@@ -1098,18 +1117,20 @@ static int __devinit omap_gpio_probe(struct platform_device *pdev)
/* Static mapping, never released */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (unlikely(!res)) {
- dev_err(&pdev->dev, "GPIO Bank %i Invalid mem resource\n",
- pdev->id);
- ret = -ENODEV;
- goto err_free;
+ dev_err(dev, "Invalid mem resource\n");
+ return -ENODEV;
}
- bank->base = ioremap(res->start, resource_size(res));
+ if (!devm_request_mem_region(dev, res->start, resource_size(res),
+ pdev->name)) {
+ dev_err(dev, "Region already claimed\n");
+ return -EBUSY;
+ }
+
+ bank->base = devm_ioremap(dev, res->start, resource_size(res));
if (!bank->base) {
- dev_err(&pdev->dev, "Could not ioremap gpio bank%i\n",
- pdev->id);
- ret = -ENOMEM;
- goto err_free;
+ dev_err(dev, "Could not ioremap\n");
+ return -ENOMEM;
}
platform_set_drvdata(pdev, bank);
@@ -1130,11 +1151,6 @@ static int __devinit omap_gpio_probe(struct platform_device *pdev)
list_add_tail(&bank->node, &omap_gpio_list);
return ret;
-
-err_free:
- kfree(bank);
-err_exit:
- return ret;
}
#ifdef CONFIG_ARCH_OMAP2PLUS
@@ -1196,8 +1212,30 @@ static int omap_gpio_runtime_suspend(struct device *dev)
struct gpio_bank *bank = platform_get_drvdata(pdev);
u32 l1 = 0, l2 = 0;
unsigned long flags;
+ u32 wake_low, wake_hi;
spin_lock_irqsave(&bank->lock, flags);
+
+ /*
+ * Only edges can generate a wakeup event to the PRCM.
+ *
+ * Therefore, ensure any wake-up capable GPIOs have
+ * edge-detection enabled before going idle to ensure a wakeup
+ * to the PRCM is generated on a GPIO transition. (c.f. 34xx
+ * NDA TRM 25.5.3.1)
+ *
+ * The normal values will be restored upon ->runtime_resume()
+ * by writing back the values saved in bank->context.
+ */
+ wake_low = bank->context.leveldetect0 & bank->context.wake_en;
+ if (wake_low)
+ __raw_writel(wake_low | bank->context.fallingdetect,
+ bank->base + bank->regs->fallingdetect);
+ wake_hi = bank->context.leveldetect1 & bank->context.wake_en;
+ if (wake_hi)
+ __raw_writel(wake_hi | bank->context.risingdetect,
+ bank->base + bank->regs->risingdetect);
+
if (bank->power_mode != OFF_MODE) {
bank->power_mode = 0;
goto update_gpio_context_count;
@@ -1207,9 +1245,6 @@ static int omap_gpio_runtime_suspend(struct device *dev)
* non-wakeup GPIOs. Otherwise spurious IRQs will be
* generated. See OMAP2420 Errata item 1.101.
*/
- if (!(bank->enabled_non_wakeup_gpios))
- goto update_gpio_context_count;
-
bank->saved_datain = __raw_readl(bank->base +
bank->regs->datain);
l1 = __raw_readl(bank->base + bank->regs->fallingdetect);
@@ -1246,7 +1281,19 @@ static int omap_gpio_runtime_resume(struct device *dev)
spin_lock_irqsave(&bank->lock, flags);
_gpio_dbck_enable(bank);
- if (!bank->enabled_non_wakeup_gpios || !bank->workaround_enabled) {
+
+ /*
+ * In ->runtime_suspend(), level-triggered, wakeup-enabled
+ * GPIOs were set to edge trigger also in order to be able to
+ * generate a PRCM wakeup. Here we restore the
+ * pre-runtime_suspend() values for edge triggering.
+ */
+ __raw_writel(bank->context.fallingdetect,
+ bank->base + bank->regs->fallingdetect);
+ __raw_writel(bank->context.risingdetect,
+ bank->base + bank->regs->risingdetect);
+
+ if (!bank->workaround_enabled) {
spin_unlock_irqrestore(&bank->lock, flags);
return 0;
}
@@ -1397,11 +1444,95 @@ static const struct dev_pm_ops gpio_pm_ops = {
NULL)
};
+#if defined(CONFIG_OF)
+static struct omap_gpio_reg_offs omap2_gpio_regs = {
+ .revision = OMAP24XX_GPIO_REVISION,
+ .direction = OMAP24XX_GPIO_OE,
+ .datain = OMAP24XX_GPIO_DATAIN,
+ .dataout = OMAP24XX_GPIO_DATAOUT,
+ .set_dataout = OMAP24XX_GPIO_SETDATAOUT,
+ .clr_dataout = OMAP24XX_GPIO_CLEARDATAOUT,
+ .irqstatus = OMAP24XX_GPIO_IRQSTATUS1,
+ .irqstatus2 = OMAP24XX_GPIO_IRQSTATUS2,
+ .irqenable = OMAP24XX_GPIO_IRQENABLE1,
+ .irqenable2 = OMAP24XX_GPIO_IRQENABLE2,
+ .set_irqenable = OMAP24XX_GPIO_SETIRQENABLE1,
+ .clr_irqenable = OMAP24XX_GPIO_CLEARIRQENABLE1,
+ .debounce = OMAP24XX_GPIO_DEBOUNCE_VAL,
+ .debounce_en = OMAP24XX_GPIO_DEBOUNCE_EN,
+ .ctrl = OMAP24XX_GPIO_CTRL,
+ .wkup_en = OMAP24XX_GPIO_WAKE_EN,
+ .leveldetect0 = OMAP24XX_GPIO_LEVELDETECT0,
+ .leveldetect1 = OMAP24XX_GPIO_LEVELDETECT1,
+ .risingdetect = OMAP24XX_GPIO_RISINGDETECT,
+ .fallingdetect = OMAP24XX_GPIO_FALLINGDETECT,
+};
+
+static struct omap_gpio_reg_offs omap4_gpio_regs = {
+ .revision = OMAP4_GPIO_REVISION,
+ .direction = OMAP4_GPIO_OE,
+ .datain = OMAP4_GPIO_DATAIN,
+ .dataout = OMAP4_GPIO_DATAOUT,
+ .set_dataout = OMAP4_GPIO_SETDATAOUT,
+ .clr_dataout = OMAP4_GPIO_CLEARDATAOUT,
+ .irqstatus = OMAP4_GPIO_IRQSTATUS0,
+ .irqstatus2 = OMAP4_GPIO_IRQSTATUS1,
+ .irqenable = OMAP4_GPIO_IRQSTATUSSET0,
+ .irqenable2 = OMAP4_GPIO_IRQSTATUSSET1,
+ .set_irqenable = OMAP4_GPIO_IRQSTATUSSET0,
+ .clr_irqenable = OMAP4_GPIO_IRQSTATUSCLR0,
+ .debounce = OMAP4_GPIO_DEBOUNCINGTIME,
+ .debounce_en = OMAP4_GPIO_DEBOUNCENABLE,
+ .ctrl = OMAP4_GPIO_CTRL,
+ .wkup_en = OMAP4_GPIO_IRQWAKEN0,
+ .leveldetect0 = OMAP4_GPIO_LEVELDETECT0,
+ .leveldetect1 = OMAP4_GPIO_LEVELDETECT1,
+ .risingdetect = OMAP4_GPIO_RISINGDETECT,
+ .fallingdetect = OMAP4_GPIO_FALLINGDETECT,
+};
+
+static struct omap_gpio_platform_data omap2_pdata = {
+ .regs = &omap2_gpio_regs,
+ .bank_width = 32,
+ .dbck_flag = false,
+};
+
+static struct omap_gpio_platform_data omap3_pdata = {
+ .regs = &omap2_gpio_regs,
+ .bank_width = 32,
+ .dbck_flag = true,
+};
+
+static struct omap_gpio_platform_data omap4_pdata = {
+ .regs = &omap4_gpio_regs,
+ .bank_width = 32,
+ .dbck_flag = true,
+};
+
+static const struct of_device_id omap_gpio_match[] = {
+ {
+ .compatible = "ti,omap4-gpio",
+ .data = &omap4_pdata,
+ },
+ {
+ .compatible = "ti,omap3-gpio",
+ .data = &omap3_pdata,
+ },
+ {
+ .compatible = "ti,omap2-gpio",
+ .data = &omap2_pdata,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, omap_gpio_match);
+#endif
+
static struct platform_driver omap_gpio_driver = {
.probe = omap_gpio_probe,
.driver = {
.name = "omap_gpio",
.pm = &gpio_pm_ops,
+ .of_match_table = of_match_ptr(omap_gpio_match),
},
};
OpenPOWER on IntegriCloud