linux_dsm_epyc7002/drivers/gpio/gpio-omap.c

1541 lines
41 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-only
/*
* Support functions for OMAP GPIO
*
* Copyright (C) 2003-2005 Nokia Corporation
* Written by Juha Yrjölä <juha.yrjola@nokia.com>
*
* Copyright (C) 2009 Texas Instruments
* Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/syscore_ops.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/cpu_pm.h>
#include <linux/device.h>
OMAP: GPIO: Implement GPIO as a platform device Implement GPIO as a platform device. GPIO APIs are used in machine_init functions. Hence it is required to complete GPIO probe before board_init. Therefore GPIO device register and driver register are implemented as postcore_initcalls. omap_gpio_init() does nothing now and this function would be removed in the next patch as it's usage is spread across most of the board files. Inorder to convert GPIO as platform device, modifications are required in clockxxxx_data.c file for OMAP1 so that device names can be used to obtain clock instead of getting clocks by name/NULL ptr. Use runtime pm APIs (pm_runtime_put*/pm_runtime_get*) for enabling or disabling the clocks, modify sysconfig settings and remove usage of clock FW APIs. Note 1: Converting GPIO driver to use runtime PM APIs is not done as a separate patch because GPIO clock names are different for various OMAPs and are different for some of the banks in the same CPU. This would need usage of cpu_is checks and bank id checks while using clock FW APIs in the gpio driver. Hence while making GPIO a platform driver framework, PM runtime APIs are used directly. Note 2: While implementing GPIO as a platform device, pm runtime APIs are used as mentioned above and modification is not done in gpio's prepare for idle/ resume after idle functions. This would be done in the next patch series and GPIO driver would be made to use dev_pm_ops instead of sysdev_class in that series only. Due to the above, the GPIO driver implicitly relies on CM_AUTOIDLE = 1 on its iclk for power management to work, since the driver never disables its iclk. This would be taken care in the next patch series (see Note 3 below). Refer to http://www.mail-archive.com/linux-omap@vger.kernel.org/msg39112.html for more details. Note 3: only pm_runtime_get_sync is called in gpio's probe() and pm_runtime_put* is never called. This is to make the implementation similar to the existing GPIO code. Another patch series would be sent to correct this. In OMAP3 and OMAP4 gpio's debounce clocks are optional clocks. They are enabled/ disabled whenever required using clock framework APIs TODO: 1. Cleanup the GPIO driver. Use function pointers and register offest pointers instead of using hardcoded values 2. Remove all cpu_is_ checks and OMAP specific macros 3. Remove usage of gpio_bank array so that only instance specific information is used in driver code 4. Rename 'method'/ avoid it's usage 5. Fix the non-wakeup gpios handling for OMAP2430, OMAP3 & OMAP4 6. Modify gpio's prepare for idle/ resume after idle functions to use runtime pm implentation. Signed-off-by: Charulatha V <charu@ti.com> Signed-off-by: Rajendra Nayak <rnayak@ti.com> Reviewed-by: Basak, Partha <p-basak2@ti.com> Acked-by: Kevin Hilman <khilman@deeprootsystems.com> [tony@atomide.com: updated for bank specific revision and updated boards] Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-12-08 07:26:57 +07:00
#include <linux/pm_runtime.h>
#include <linux/pm.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/gpio/driver.h>
#include <linux/bitops.h>
#include <linux/platform_data/gpio-omap.h>
#define OMAP4_GPIO_DEBOUNCINGTIME_MASK 0xFF
struct gpio_regs {
u32 irqenable1;
u32 irqenable2;
u32 wake_en;
u32 ctrl;
u32 oe;
u32 leveldetect0;
u32 leveldetect1;
u32 risingdetect;
u32 fallingdetect;
u32 dataout;
u32 debounce;
u32 debounce_en;
};
struct gpio_bank {
void __iomem *base;
const struct omap_gpio_reg_offs *regs;
int irq;
u32 non_wakeup_gpios;
u32 enabled_non_wakeup_gpios;
struct gpio_regs context;
u32 saved_datain;
u32 level_mask;
omap: gpio: Simultaneously requested rising and falling edge Some chips, namely any OMAP1 chips using METHOD_MPUIO, OMAP15xx and OMAP7xx, cannot be setup to respond to on-chip GPIO interrupts in both rising and falling edge directions -- they can only respond to one direction or the other, depending on how the ICR is configured. Additionally, current code forces rising edge detection if both flags are specified: if (trigger & IRQ_TYPE_EDGE_RISING) l |= 1 << gpio; else if (trigger & IRQ_TYPE_EDGE_FALLING) l &= ~(1 << gpio); else goto bad; This change implements a toggle function that will modify the ICR to flip the direction of interrupt for IRQs that are requested with both rising and falling flags. The toggle function is not called for chips and GPIOs it does not apply to through the use of a flip_mask that's added on a per-bank basis. The mask is only set for those GPIOs where a toggle is necessary. Edge detection starts out the same as above with FALLING mode first. The toggle happens on EACH interrupt; without it, we have the following sequence of actions on GPIO transition: ICR GPIO Result 0x1 0 -> 1 (rising) Interrupt 0x1 1 -> 0 (falling) No interrupt (set ICR to 0x0 manually) 0x0 0 -> 1 (rising) No interrupt 0x0 1 -> 0 (falling) Interrupt That is, with the ICR set to 1 for a gpio, only rising edge interrupts are caught, and with it set to 0, only falling edge interrupts are caught. If we add in the toggle, we get this: ICR GPIO Result 0x1 0 -> 1 (rising) Interrupt (ICR set to 0x0) 0x0 1 -> 0 (falling) Interrupt (ICR set to 0x1) 0x1 0 -> 1 ... so, both rising and falling are caught, per the request for both (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING). Signed-off-by: Cory Maccarrone <darkstar6262@gmail.com> Acked-by: Kevin Hilman <khilman@deeprootsystems.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-01-09 01:29:04 +07:00
u32 toggle_mask;
raw_spinlock_t lock;
gpio: omap: convert to use generic irq handler This patch converts TI OMAP GPIO driver to use generic irq handler instead of chained IRQ handler. This way OMAP GPIO driver will be compatible with RT kernel where it will be forced thread IRQ handler while in non-RT kernel it still will be executed in HW IRQ context. As part of this change the IRQ wakeup configuration is applied to GPIO Bank IRQ as it now will be under control of IRQ PM Core during suspend. There are also additional benefits: - on-RT kernel there will be no complains any more about PM runtime usage in atomic context "BUG: sleeping function called from invalid context"; - GPIO bank IRQs will appear in /proc/interrupts and its usage statistic will be visible; - GPIO bank IRQs could be configured through IRQ proc_fs interface and, as result, could be a part of IRQ balancing process if needed; - GPIO bank IRQs will be under control of IRQ PM Core during suspend to RAM. Disadvantage: - additional runtime overhed as call chain till omap_gpio_irq_handler() will be longer now - necessity to use wa_lock in omap_gpio_irq_handler() to W/A warning in handle_irq_event_percpu() WARNING: CPU: 1 PID: 35 at kernel/irq/handle.c:149 handle_irq_event_percpu+0x51c/0x638() This patch doesn't fully follows recommendations provided by Sebastian Andrzej Siewior [1], because It's required to go through and check all GPIO IRQ pin states as fast as possible and pass control to handle_level_irq or handle_edge_irq. handle_level_irq or handle_edge_irq will perform actions specific for IRQ triggering type and wakeup corresponding registered threaded IRQ handler (at least it's expected to be threaded). IRQs can be lost if handle_nested_irq() will be used, because excecution time of some pin specific GPIO IRQ handler can be very significant and require accessing ext. devices (I2C). Idea of such kind reworking was also discussed in [2]. [1] http://www.spinics.net/lists/linux-omap/msg120665.html [2] http://www.spinics.net/lists/linux-omap/msg119516.html Tested-by: Tony Lindgren <tony@atomide.com> Tested-by: Austin Schuh <austin@peloton-tech.com> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com> Acked-by: Santosh Shilimkar <ssantosh@kernel.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2015-09-26 02:28:03 +07:00
raw_spinlock_t wa_lock;
struct gpio_chip chip;
struct clk *dbck;
struct notifier_block nb;
unsigned int is_suspended:1;
u32 mod_usage;
u32 irq_usage;
u32 dbck_enable_mask;
bool dbck_enabled;
bool is_mpuio;
OMAP: GPIO: Implement GPIO as a platform device Implement GPIO as a platform device. GPIO APIs are used in machine_init functions. Hence it is required to complete GPIO probe before board_init. Therefore GPIO device register and driver register are implemented as postcore_initcalls. omap_gpio_init() does nothing now and this function would be removed in the next patch as it's usage is spread across most of the board files. Inorder to convert GPIO as platform device, modifications are required in clockxxxx_data.c file for OMAP1 so that device names can be used to obtain clock instead of getting clocks by name/NULL ptr. Use runtime pm APIs (pm_runtime_put*/pm_runtime_get*) for enabling or disabling the clocks, modify sysconfig settings and remove usage of clock FW APIs. Note 1: Converting GPIO driver to use runtime PM APIs is not done as a separate patch because GPIO clock names are different for various OMAPs and are different for some of the banks in the same CPU. This would need usage of cpu_is checks and bank id checks while using clock FW APIs in the gpio driver. Hence while making GPIO a platform driver framework, PM runtime APIs are used directly. Note 2: While implementing GPIO as a platform device, pm runtime APIs are used as mentioned above and modification is not done in gpio's prepare for idle/ resume after idle functions. This would be done in the next patch series and GPIO driver would be made to use dev_pm_ops instead of sysdev_class in that series only. Due to the above, the GPIO driver implicitly relies on CM_AUTOIDLE = 1 on its iclk for power management to work, since the driver never disables its iclk. This would be taken care in the next patch series (see Note 3 below). Refer to http://www.mail-archive.com/linux-omap@vger.kernel.org/msg39112.html for more details. Note 3: only pm_runtime_get_sync is called in gpio's probe() and pm_runtime_put* is never called. This is to make the implementation similar to the existing GPIO code. Another patch series would be sent to correct this. In OMAP3 and OMAP4 gpio's debounce clocks are optional clocks. They are enabled/ disabled whenever required using clock framework APIs TODO: 1. Cleanup the GPIO driver. Use function pointers and register offest pointers instead of using hardcoded values 2. Remove all cpu_is_ checks and OMAP specific macros 3. Remove usage of gpio_bank array so that only instance specific information is used in driver code 4. Rename 'method'/ avoid it's usage 5. Fix the non-wakeup gpios handling for OMAP2430, OMAP3 & OMAP4 6. Modify gpio's prepare for idle/ resume after idle functions to use runtime pm implentation. Signed-off-by: Charulatha V <charu@ti.com> Signed-off-by: Rajendra Nayak <rnayak@ti.com> Reviewed-by: Basak, Partha <p-basak2@ti.com> Acked-by: Kevin Hilman <khilman@deeprootsystems.com> [tony@atomide.com: updated for bank specific revision and updated boards] Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-12-08 07:26:57 +07:00
bool dbck_flag;
bool loses_context;
bool context_valid;
int stride;
u32 width;
int context_loss_count;
void (*set_dataout)(struct gpio_bank *bank, unsigned gpio, int enable);
int (*get_context_loss_count)(struct device *dev);
};
#define GPIO_MOD_CTRL_BIT BIT(0)
#define BANK_USED(bank) (bank->mod_usage || bank->irq_usage)
#define LINE_USED(line, offset) (line & (BIT(offset)))
2015-01-17 05:50:50 +07:00
static void omap_gpio_unmask_irq(struct irq_data *d);
static inline struct gpio_bank *omap_irq_data_get_bank(struct irq_data *d)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
return gpiochip_get_data(chip);
}
static inline u32 omap_gpio_rmw(void __iomem *reg, u32 mask, bool set)
{
u32 val = readl_relaxed(reg);
if (set)
val |= mask;
else
val &= ~mask;
writel_relaxed(val, reg);
return val;
}
static void omap_set_gpio_direction(struct gpio_bank *bank, int gpio,
int is_input)
{
bank->context.oe = omap_gpio_rmw(bank->base + bank->regs->direction,
BIT(gpio), is_input);
}
/* set data out value using dedicate set/clear register */
static void omap_set_gpio_dataout_reg(struct gpio_bank *bank, unsigned offset,
int enable)
{
void __iomem *reg = bank->base;
u32 l = BIT(offset);
if (enable) {
reg += bank->regs->set_dataout;
bank->context.dataout |= l;
} else {
reg += bank->regs->clr_dataout;
bank->context.dataout &= ~l;
}
writel_relaxed(l, reg);
}
/* set data out value using mask register */
static void omap_set_gpio_dataout_mask(struct gpio_bank *bank, unsigned offset,
int enable)
{
bank->context.dataout = omap_gpio_rmw(bank->base + bank->regs->dataout,
BIT(offset), enable);
}
static inline void omap_gpio_dbck_enable(struct gpio_bank *bank)
{
if (bank->dbck_enable_mask && !bank->dbck_enabled) {
clk_enable(bank->dbck);
bank->dbck_enabled = true;
writel_relaxed(bank->dbck_enable_mask,
bank->base + bank->regs->debounce_en);
}
}
static inline void omap_gpio_dbck_disable(struct gpio_bank *bank)
{
if (bank->dbck_enable_mask && bank->dbck_enabled) {
/*
* Disable debounce before cutting it's clock. If debounce is
* enabled but the clock is not, GPIO module seems to be unable
* to detect events and generate interrupts at least on OMAP3.
*/
writel_relaxed(0, bank->base + bank->regs->debounce_en);
clk_disable(bank->dbck);
bank->dbck_enabled = false;
}
}
/**
* omap2_set_gpio_debounce - low level gpio debounce time
* @bank: the gpio bank we're acting upon
* @offset: the gpio number on this @bank
* @debounce: debounce time to use
*
* OMAP's debounce time is in 31us steps
* <debounce time> = (GPIO_DEBOUNCINGTIME[7:0].DEBOUNCETIME + 1) x 31
* so we need to convert and round up to the closest unit.
*
* Return: 0 on success, negative error otherwise.
*/
static int omap2_set_gpio_debounce(struct gpio_bank *bank, unsigned offset,
unsigned debounce)
{
u32 val;
u32 l;
bool enable = !!debounce;
OMAP: GPIO: Implement GPIO as a platform device Implement GPIO as a platform device. GPIO APIs are used in machine_init functions. Hence it is required to complete GPIO probe before board_init. Therefore GPIO device register and driver register are implemented as postcore_initcalls. omap_gpio_init() does nothing now and this function would be removed in the next patch as it's usage is spread across most of the board files. Inorder to convert GPIO as platform device, modifications are required in clockxxxx_data.c file for OMAP1 so that device names can be used to obtain clock instead of getting clocks by name/NULL ptr. Use runtime pm APIs (pm_runtime_put*/pm_runtime_get*) for enabling or disabling the clocks, modify sysconfig settings and remove usage of clock FW APIs. Note 1: Converting GPIO driver to use runtime PM APIs is not done as a separate patch because GPIO clock names are different for various OMAPs and are different for some of the banks in the same CPU. This would need usage of cpu_is checks and bank id checks while using clock FW APIs in the gpio driver. Hence while making GPIO a platform driver framework, PM runtime APIs are used directly. Note 2: While implementing GPIO as a platform device, pm runtime APIs are used as mentioned above and modification is not done in gpio's prepare for idle/ resume after idle functions. This would be done in the next patch series and GPIO driver would be made to use dev_pm_ops instead of sysdev_class in that series only. Due to the above, the GPIO driver implicitly relies on CM_AUTOIDLE = 1 on its iclk for power management to work, since the driver never disables its iclk. This would be taken care in the next patch series (see Note 3 below). Refer to http://www.mail-archive.com/linux-omap@vger.kernel.org/msg39112.html for more details. Note 3: only pm_runtime_get_sync is called in gpio's probe() and pm_runtime_put* is never called. This is to make the implementation similar to the existing GPIO code. Another patch series would be sent to correct this. In OMAP3 and OMAP4 gpio's debounce clocks are optional clocks. They are enabled/ disabled whenever required using clock framework APIs TODO: 1. Cleanup the GPIO driver. Use function pointers and register offest pointers instead of using hardcoded values 2. Remove all cpu_is_ checks and OMAP specific macros 3. Remove usage of gpio_bank array so that only instance specific information is used in driver code 4. Rename 'method'/ avoid it's usage 5. Fix the non-wakeup gpios handling for OMAP2430, OMAP3 & OMAP4 6. Modify gpio's prepare for idle/ resume after idle functions to use runtime pm implentation. Signed-off-by: Charulatha V <charu@ti.com> Signed-off-by: Rajendra Nayak <rnayak@ti.com> Reviewed-by: Basak, Partha <p-basak2@ti.com> Acked-by: Kevin Hilman <khilman@deeprootsystems.com> [tony@atomide.com: updated for bank specific revision and updated boards] Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-12-08 07:26:57 +07:00
if (!bank->dbck_flag)
return -ENOTSUPP;
OMAP: GPIO: Implement GPIO as a platform device Implement GPIO as a platform device. GPIO APIs are used in machine_init functions. Hence it is required to complete GPIO probe before board_init. Therefore GPIO device register and driver register are implemented as postcore_initcalls. omap_gpio_init() does nothing now and this function would be removed in the next patch as it's usage is spread across most of the board files. Inorder to convert GPIO as platform device, modifications are required in clockxxxx_data.c file for OMAP1 so that device names can be used to obtain clock instead of getting clocks by name/NULL ptr. Use runtime pm APIs (pm_runtime_put*/pm_runtime_get*) for enabling or disabling the clocks, modify sysconfig settings and remove usage of clock FW APIs. Note 1: Converting GPIO driver to use runtime PM APIs is not done as a separate patch because GPIO clock names are different for various OMAPs and are different for some of the banks in the same CPU. This would need usage of cpu_is checks and bank id checks while using clock FW APIs in the gpio driver. Hence while making GPIO a platform driver framework, PM runtime APIs are used directly. Note 2: While implementing GPIO as a platform device, pm runtime APIs are used as mentioned above and modification is not done in gpio's prepare for idle/ resume after idle functions. This would be done in the next patch series and GPIO driver would be made to use dev_pm_ops instead of sysdev_class in that series only. Due to the above, the GPIO driver implicitly relies on CM_AUTOIDLE = 1 on its iclk for power management to work, since the driver never disables its iclk. This would be taken care in the next patch series (see Note 3 below). Refer to http://www.mail-archive.com/linux-omap@vger.kernel.org/msg39112.html for more details. Note 3: only pm_runtime_get_sync is called in gpio's probe() and pm_runtime_put* is never called. This is to make the implementation similar to the existing GPIO code. Another patch series would be sent to correct this. In OMAP3 and OMAP4 gpio's debounce clocks are optional clocks. They are enabled/ disabled whenever required using clock framework APIs TODO: 1. Cleanup the GPIO driver. Use function pointers and register offest pointers instead of using hardcoded values 2. Remove all cpu_is_ checks and OMAP specific macros 3. Remove usage of gpio_bank array so that only instance specific information is used in driver code 4. Rename 'method'/ avoid it's usage 5. Fix the non-wakeup gpios handling for OMAP2430, OMAP3 & OMAP4 6. Modify gpio's prepare for idle/ resume after idle functions to use runtime pm implentation. Signed-off-by: Charulatha V <charu@ti.com> Signed-off-by: Rajendra Nayak <rnayak@ti.com> Reviewed-by: Basak, Partha <p-basak2@ti.com> Acked-by: Kevin Hilman <khilman@deeprootsystems.com> [tony@atomide.com: updated for bank specific revision and updated boards] Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-12-08 07:26:57 +07:00
if (enable) {
debounce = DIV_ROUND_UP(debounce, 31) - 1;
if ((debounce & OMAP4_GPIO_DEBOUNCINGTIME_MASK) != debounce)
return -EINVAL;
}
l = BIT(offset);
clk_enable(bank->dbck);
writel_relaxed(debounce, bank->base + bank->regs->debounce);
val = omap_gpio_rmw(bank->base + bank->regs->debounce_en, l, enable);
bank->dbck_enable_mask = val;
clk_disable(bank->dbck);
/*
* Enable debounce clock per module.
* This call is mandatory because in omap_gpio_request() when
* *_runtime_get_sync() is called, _gpio_dbck_enable() within
* runtime callbck fails to turn on dbck because dbck_enable_mask
* used within _gpio_dbck_enable() is still not initialized at
* that point. Therefore we have to enable dbck here.
*/
omap_gpio_dbck_enable(bank);
if (bank->dbck_enable_mask) {
bank->context.debounce = debounce;
bank->context.debounce_en = val;
}
return 0;
}
gpio/omap: fix off-mode bug: clear debounce settings on free/reset This change was originally titled "gpio/omap: fix off-mode bug: clear debounce clock enable mask on free/reset". The title has been updated slightly to reflect (what should be) the final fix. When a GPIO is freed or shutdown, we need to ensure that any debounce settings are cleared and if the GPIO is the only GPIO in the bank that is currently using debounce, then disable the debounce clock as well to save power. Currently, the debounce settings are not cleared on a GPIO free or shutdown and so during a context restore on subsequent off-mode transition, the previous debounce values are restored from the shadow copies (bank->context.debounce*) leading to mismatch state between driver state and hardware state. This was discovered when board code was doing gpio_request_one() gpio_set_debounce() gpio_free() which was leaving the GPIO debounce settings in a confused state. If that GPIO bank is subsequently used with off-mode enabled, bogus state would be restored, leaving GPIO debounce enabled which then prevented the CORE powerdomain from transitioning. To fix this, introduce a new function called _clear_gpio_debounce() to clear any debounce settings when the GPIO is freed or shutdown. If this GPIO is the last debounce-enabled GPIO in the bank, the debounce will also be cut. Please note that we cannot use _gpio_dbck_disable() to disable the debounce clock because this has been specifically created for the gpio suspend path and is intended to shutdown the debounce clock while debounce is enabled. Special thanks to Kevin Hilman for root causing the bug. This fix is a collaborative effort with inputs from Kevin Hilman, Grazvydas Ignotas and Santosh Shilimkar. Testing: - This has been unit tested on an OMAP3430 Beagle board, by requesting a gpio, enabling debounce and then freeing the gpio and checking the register contents, the saved register context and the debounce clock state. - Kevin Hilman tested on 37xx/EVM board which configures GPIO debounce for the ads7846 touchscreen in its board file using the above sequence, and so was failing off-mode tests in dynamic idle. Verified that off-mode tests are passing with this patch. V5 changes: - Corrected author Reported-by: Paul Walmsley <paul@pwsan.com> Cc: Igor Grinberg <grinberg@compulab.co.il> Cc: Grazvydas Ignotas <notasas@gmail.com> Cc: Jon Hunter <jon-hunter@ti.com> Signed-off-by: Jon Hunter <jon-hunter@ti.com> Reviewed-by: Kevin Hilman <khilman@ti.com> Tested-by: Kevin Hilman <khilman@ti.com> Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2012-10-27 02:26:04 +07:00
/**
* omap_clear_gpio_debounce - clear debounce settings for a gpio
gpio/omap: fix off-mode bug: clear debounce settings on free/reset This change was originally titled "gpio/omap: fix off-mode bug: clear debounce clock enable mask on free/reset". The title has been updated slightly to reflect (what should be) the final fix. When a GPIO is freed or shutdown, we need to ensure that any debounce settings are cleared and if the GPIO is the only GPIO in the bank that is currently using debounce, then disable the debounce clock as well to save power. Currently, the debounce settings are not cleared on a GPIO free or shutdown and so during a context restore on subsequent off-mode transition, the previous debounce values are restored from the shadow copies (bank->context.debounce*) leading to mismatch state between driver state and hardware state. This was discovered when board code was doing gpio_request_one() gpio_set_debounce() gpio_free() which was leaving the GPIO debounce settings in a confused state. If that GPIO bank is subsequently used with off-mode enabled, bogus state would be restored, leaving GPIO debounce enabled which then prevented the CORE powerdomain from transitioning. To fix this, introduce a new function called _clear_gpio_debounce() to clear any debounce settings when the GPIO is freed or shutdown. If this GPIO is the last debounce-enabled GPIO in the bank, the debounce will also be cut. Please note that we cannot use _gpio_dbck_disable() to disable the debounce clock because this has been specifically created for the gpio suspend path and is intended to shutdown the debounce clock while debounce is enabled. Special thanks to Kevin Hilman for root causing the bug. This fix is a collaborative effort with inputs from Kevin Hilman, Grazvydas Ignotas and Santosh Shilimkar. Testing: - This has been unit tested on an OMAP3430 Beagle board, by requesting a gpio, enabling debounce and then freeing the gpio and checking the register contents, the saved register context and the debounce clock state. - Kevin Hilman tested on 37xx/EVM board which configures GPIO debounce for the ads7846 touchscreen in its board file using the above sequence, and so was failing off-mode tests in dynamic idle. Verified that off-mode tests are passing with this patch. V5 changes: - Corrected author Reported-by: Paul Walmsley <paul@pwsan.com> Cc: Igor Grinberg <grinberg@compulab.co.il> Cc: Grazvydas Ignotas <notasas@gmail.com> Cc: Jon Hunter <jon-hunter@ti.com> Signed-off-by: Jon Hunter <jon-hunter@ti.com> Reviewed-by: Kevin Hilman <khilman@ti.com> Tested-by: Kevin Hilman <khilman@ti.com> Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2012-10-27 02:26:04 +07:00
* @bank: the gpio bank we're acting upon
* @offset: the gpio number on this @bank
gpio/omap: fix off-mode bug: clear debounce settings on free/reset This change was originally titled "gpio/omap: fix off-mode bug: clear debounce clock enable mask on free/reset". The title has been updated slightly to reflect (what should be) the final fix. When a GPIO is freed or shutdown, we need to ensure that any debounce settings are cleared and if the GPIO is the only GPIO in the bank that is currently using debounce, then disable the debounce clock as well to save power. Currently, the debounce settings are not cleared on a GPIO free or shutdown and so during a context restore on subsequent off-mode transition, the previous debounce values are restored from the shadow copies (bank->context.debounce*) leading to mismatch state between driver state and hardware state. This was discovered when board code was doing gpio_request_one() gpio_set_debounce() gpio_free() which was leaving the GPIO debounce settings in a confused state. If that GPIO bank is subsequently used with off-mode enabled, bogus state would be restored, leaving GPIO debounce enabled which then prevented the CORE powerdomain from transitioning. To fix this, introduce a new function called _clear_gpio_debounce() to clear any debounce settings when the GPIO is freed or shutdown. If this GPIO is the last debounce-enabled GPIO in the bank, the debounce will also be cut. Please note that we cannot use _gpio_dbck_disable() to disable the debounce clock because this has been specifically created for the gpio suspend path and is intended to shutdown the debounce clock while debounce is enabled. Special thanks to Kevin Hilman for root causing the bug. This fix is a collaborative effort with inputs from Kevin Hilman, Grazvydas Ignotas and Santosh Shilimkar. Testing: - This has been unit tested on an OMAP3430 Beagle board, by requesting a gpio, enabling debounce and then freeing the gpio and checking the register contents, the saved register context and the debounce clock state. - Kevin Hilman tested on 37xx/EVM board which configures GPIO debounce for the ads7846 touchscreen in its board file using the above sequence, and so was failing off-mode tests in dynamic idle. Verified that off-mode tests are passing with this patch. V5 changes: - Corrected author Reported-by: Paul Walmsley <paul@pwsan.com> Cc: Igor Grinberg <grinberg@compulab.co.il> Cc: Grazvydas Ignotas <notasas@gmail.com> Cc: Jon Hunter <jon-hunter@ti.com> Signed-off-by: Jon Hunter <jon-hunter@ti.com> Reviewed-by: Kevin Hilman <khilman@ti.com> Tested-by: Kevin Hilman <khilman@ti.com> Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2012-10-27 02:26:04 +07:00
*
* If a gpio is using debounce, then clear the debounce enable bit and if
* this is the only gpio in this bank using debounce, then clear the debounce
* time too. The debounce clock will also be disabled when calling this function
* if this is the only gpio in the bank using debounce.
*/
static void omap_clear_gpio_debounce(struct gpio_bank *bank, unsigned offset)
gpio/omap: fix off-mode bug: clear debounce settings on free/reset This change was originally titled "gpio/omap: fix off-mode bug: clear debounce clock enable mask on free/reset". The title has been updated slightly to reflect (what should be) the final fix. When a GPIO is freed or shutdown, we need to ensure that any debounce settings are cleared and if the GPIO is the only GPIO in the bank that is currently using debounce, then disable the debounce clock as well to save power. Currently, the debounce settings are not cleared on a GPIO free or shutdown and so during a context restore on subsequent off-mode transition, the previous debounce values are restored from the shadow copies (bank->context.debounce*) leading to mismatch state between driver state and hardware state. This was discovered when board code was doing gpio_request_one() gpio_set_debounce() gpio_free() which was leaving the GPIO debounce settings in a confused state. If that GPIO bank is subsequently used with off-mode enabled, bogus state would be restored, leaving GPIO debounce enabled which then prevented the CORE powerdomain from transitioning. To fix this, introduce a new function called _clear_gpio_debounce() to clear any debounce settings when the GPIO is freed or shutdown. If this GPIO is the last debounce-enabled GPIO in the bank, the debounce will also be cut. Please note that we cannot use _gpio_dbck_disable() to disable the debounce clock because this has been specifically created for the gpio suspend path and is intended to shutdown the debounce clock while debounce is enabled. Special thanks to Kevin Hilman for root causing the bug. This fix is a collaborative effort with inputs from Kevin Hilman, Grazvydas Ignotas and Santosh Shilimkar. Testing: - This has been unit tested on an OMAP3430 Beagle board, by requesting a gpio, enabling debounce and then freeing the gpio and checking the register contents, the saved register context and the debounce clock state. - Kevin Hilman tested on 37xx/EVM board which configures GPIO debounce for the ads7846 touchscreen in its board file using the above sequence, and so was failing off-mode tests in dynamic idle. Verified that off-mode tests are passing with this patch. V5 changes: - Corrected author Reported-by: Paul Walmsley <paul@pwsan.com> Cc: Igor Grinberg <grinberg@compulab.co.il> Cc: Grazvydas Ignotas <notasas@gmail.com> Cc: Jon Hunter <jon-hunter@ti.com> Signed-off-by: Jon Hunter <jon-hunter@ti.com> Reviewed-by: Kevin Hilman <khilman@ti.com> Tested-by: Kevin Hilman <khilman@ti.com> Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2012-10-27 02:26:04 +07:00
{
u32 gpio_bit = BIT(offset);
gpio/omap: fix off-mode bug: clear debounce settings on free/reset This change was originally titled "gpio/omap: fix off-mode bug: clear debounce clock enable mask on free/reset". The title has been updated slightly to reflect (what should be) the final fix. When a GPIO is freed or shutdown, we need to ensure that any debounce settings are cleared and if the GPIO is the only GPIO in the bank that is currently using debounce, then disable the debounce clock as well to save power. Currently, the debounce settings are not cleared on a GPIO free or shutdown and so during a context restore on subsequent off-mode transition, the previous debounce values are restored from the shadow copies (bank->context.debounce*) leading to mismatch state between driver state and hardware state. This was discovered when board code was doing gpio_request_one() gpio_set_debounce() gpio_free() which was leaving the GPIO debounce settings in a confused state. If that GPIO bank is subsequently used with off-mode enabled, bogus state would be restored, leaving GPIO debounce enabled which then prevented the CORE powerdomain from transitioning. To fix this, introduce a new function called _clear_gpio_debounce() to clear any debounce settings when the GPIO is freed or shutdown. If this GPIO is the last debounce-enabled GPIO in the bank, the debounce will also be cut. Please note that we cannot use _gpio_dbck_disable() to disable the debounce clock because this has been specifically created for the gpio suspend path and is intended to shutdown the debounce clock while debounce is enabled. Special thanks to Kevin Hilman for root causing the bug. This fix is a collaborative effort with inputs from Kevin Hilman, Grazvydas Ignotas and Santosh Shilimkar. Testing: - This has been unit tested on an OMAP3430 Beagle board, by requesting a gpio, enabling debounce and then freeing the gpio and checking the register contents, the saved register context and the debounce clock state. - Kevin Hilman tested on 37xx/EVM board which configures GPIO debounce for the ads7846 touchscreen in its board file using the above sequence, and so was failing off-mode tests in dynamic idle. Verified that off-mode tests are passing with this patch. V5 changes: - Corrected author Reported-by: Paul Walmsley <paul@pwsan.com> Cc: Igor Grinberg <grinberg@compulab.co.il> Cc: Grazvydas Ignotas <notasas@gmail.com> Cc: Jon Hunter <jon-hunter@ti.com> Signed-off-by: Jon Hunter <jon-hunter@ti.com> Reviewed-by: Kevin Hilman <khilman@ti.com> Tested-by: Kevin Hilman <khilman@ti.com> Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2012-10-27 02:26:04 +07:00
if (!bank->dbck_flag)
return;
if (!(bank->dbck_enable_mask & gpio_bit))
return;
bank->dbck_enable_mask &= ~gpio_bit;
bank->context.debounce_en &= ~gpio_bit;
writel_relaxed(bank->context.debounce_en,
gpio/omap: fix off-mode bug: clear debounce settings on free/reset This change was originally titled "gpio/omap: fix off-mode bug: clear debounce clock enable mask on free/reset". The title has been updated slightly to reflect (what should be) the final fix. When a GPIO is freed or shutdown, we need to ensure that any debounce settings are cleared and if the GPIO is the only GPIO in the bank that is currently using debounce, then disable the debounce clock as well to save power. Currently, the debounce settings are not cleared on a GPIO free or shutdown and so during a context restore on subsequent off-mode transition, the previous debounce values are restored from the shadow copies (bank->context.debounce*) leading to mismatch state between driver state and hardware state. This was discovered when board code was doing gpio_request_one() gpio_set_debounce() gpio_free() which was leaving the GPIO debounce settings in a confused state. If that GPIO bank is subsequently used with off-mode enabled, bogus state would be restored, leaving GPIO debounce enabled which then prevented the CORE powerdomain from transitioning. To fix this, introduce a new function called _clear_gpio_debounce() to clear any debounce settings when the GPIO is freed or shutdown. If this GPIO is the last debounce-enabled GPIO in the bank, the debounce will also be cut. Please note that we cannot use _gpio_dbck_disable() to disable the debounce clock because this has been specifically created for the gpio suspend path and is intended to shutdown the debounce clock while debounce is enabled. Special thanks to Kevin Hilman for root causing the bug. This fix is a collaborative effort with inputs from Kevin Hilman, Grazvydas Ignotas and Santosh Shilimkar. Testing: - This has been unit tested on an OMAP3430 Beagle board, by requesting a gpio, enabling debounce and then freeing the gpio and checking the register contents, the saved register context and the debounce clock state. - Kevin Hilman tested on 37xx/EVM board which configures GPIO debounce for the ads7846 touchscreen in its board file using the above sequence, and so was failing off-mode tests in dynamic idle. Verified that off-mode tests are passing with this patch. V5 changes: - Corrected author Reported-by: Paul Walmsley <paul@pwsan.com> Cc: Igor Grinberg <grinberg@compulab.co.il> Cc: Grazvydas Ignotas <notasas@gmail.com> Cc: Jon Hunter <jon-hunter@ti.com> Signed-off-by: Jon Hunter <jon-hunter@ti.com> Reviewed-by: Kevin Hilman <khilman@ti.com> Tested-by: Kevin Hilman <khilman@ti.com> Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2012-10-27 02:26:04 +07:00
bank->base + bank->regs->debounce_en);
if (!bank->dbck_enable_mask) {
bank->context.debounce = 0;
writel_relaxed(bank->context.debounce, bank->base +
gpio/omap: fix off-mode bug: clear debounce settings on free/reset This change was originally titled "gpio/omap: fix off-mode bug: clear debounce clock enable mask on free/reset". The title has been updated slightly to reflect (what should be) the final fix. When a GPIO is freed or shutdown, we need to ensure that any debounce settings are cleared and if the GPIO is the only GPIO in the bank that is currently using debounce, then disable the debounce clock as well to save power. Currently, the debounce settings are not cleared on a GPIO free or shutdown and so during a context restore on subsequent off-mode transition, the previous debounce values are restored from the shadow copies (bank->context.debounce*) leading to mismatch state between driver state and hardware state. This was discovered when board code was doing gpio_request_one() gpio_set_debounce() gpio_free() which was leaving the GPIO debounce settings in a confused state. If that GPIO bank is subsequently used with off-mode enabled, bogus state would be restored, leaving GPIO debounce enabled which then prevented the CORE powerdomain from transitioning. To fix this, introduce a new function called _clear_gpio_debounce() to clear any debounce settings when the GPIO is freed or shutdown. If this GPIO is the last debounce-enabled GPIO in the bank, the debounce will also be cut. Please note that we cannot use _gpio_dbck_disable() to disable the debounce clock because this has been specifically created for the gpio suspend path and is intended to shutdown the debounce clock while debounce is enabled. Special thanks to Kevin Hilman for root causing the bug. This fix is a collaborative effort with inputs from Kevin Hilman, Grazvydas Ignotas and Santosh Shilimkar. Testing: - This has been unit tested on an OMAP3430 Beagle board, by requesting a gpio, enabling debounce and then freeing the gpio and checking the register contents, the saved register context and the debounce clock state. - Kevin Hilman tested on 37xx/EVM board which configures GPIO debounce for the ads7846 touchscreen in its board file using the above sequence, and so was failing off-mode tests in dynamic idle. Verified that off-mode tests are passing with this patch. V5 changes: - Corrected author Reported-by: Paul Walmsley <paul@pwsan.com> Cc: Igor Grinberg <grinberg@compulab.co.il> Cc: Grazvydas Ignotas <notasas@gmail.com> Cc: Jon Hunter <jon-hunter@ti.com> Signed-off-by: Jon Hunter <jon-hunter@ti.com> Reviewed-by: Kevin Hilman <khilman@ti.com> Tested-by: Kevin Hilman <khilman@ti.com> Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2012-10-27 02:26:04 +07:00
bank->regs->debounce);
clk_disable(bank->dbck);
gpio/omap: fix off-mode bug: clear debounce settings on free/reset This change was originally titled "gpio/omap: fix off-mode bug: clear debounce clock enable mask on free/reset". The title has been updated slightly to reflect (what should be) the final fix. When a GPIO is freed or shutdown, we need to ensure that any debounce settings are cleared and if the GPIO is the only GPIO in the bank that is currently using debounce, then disable the debounce clock as well to save power. Currently, the debounce settings are not cleared on a GPIO free or shutdown and so during a context restore on subsequent off-mode transition, the previous debounce values are restored from the shadow copies (bank->context.debounce*) leading to mismatch state between driver state and hardware state. This was discovered when board code was doing gpio_request_one() gpio_set_debounce() gpio_free() which was leaving the GPIO debounce settings in a confused state. If that GPIO bank is subsequently used with off-mode enabled, bogus state would be restored, leaving GPIO debounce enabled which then prevented the CORE powerdomain from transitioning. To fix this, introduce a new function called _clear_gpio_debounce() to clear any debounce settings when the GPIO is freed or shutdown. If this GPIO is the last debounce-enabled GPIO in the bank, the debounce will also be cut. Please note that we cannot use _gpio_dbck_disable() to disable the debounce clock because this has been specifically created for the gpio suspend path and is intended to shutdown the debounce clock while debounce is enabled. Special thanks to Kevin Hilman for root causing the bug. This fix is a collaborative effort with inputs from Kevin Hilman, Grazvydas Ignotas and Santosh Shilimkar. Testing: - This has been unit tested on an OMAP3430 Beagle board, by requesting a gpio, enabling debounce and then freeing the gpio and checking the register contents, the saved register context and the debounce clock state. - Kevin Hilman tested on 37xx/EVM board which configures GPIO debounce for the ads7846 touchscreen in its board file using the above sequence, and so was failing off-mode tests in dynamic idle. Verified that off-mode tests are passing with this patch. V5 changes: - Corrected author Reported-by: Paul Walmsley <paul@pwsan.com> Cc: Igor Grinberg <grinberg@compulab.co.il> Cc: Grazvydas Ignotas <notasas@gmail.com> Cc: Jon Hunter <jon-hunter@ti.com> Signed-off-by: Jon Hunter <jon-hunter@ti.com> Reviewed-by: Kevin Hilman <khilman@ti.com> Tested-by: Kevin Hilman <khilman@ti.com> Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2012-10-27 02:26:04 +07:00
bank->dbck_enabled = false;
}
}
/*
* Off mode wake-up capable GPIOs in bank(s) that are in the wakeup domain.
* See TRM section for GPIO for "Wake-Up Generation" for the list of GPIOs
* in wakeup domain. If bank->non_wakeup_gpios is not configured, assume none
* are capable waking up the system from off mode.
*/
static bool omap_gpio_is_off_wakeup_capable(struct gpio_bank *bank, u32 gpio_mask)
{
u32 no_wake = bank->non_wakeup_gpios;
if (no_wake)
return !!(~no_wake & gpio_mask);
return false;
}
static inline void omap_set_gpio_trigger(struct gpio_bank *bank, int gpio,
unsigned trigger)
{
void __iomem *base = bank->base;
u32 gpio_bit = BIT(gpio);
omap_gpio_rmw(base + bank->regs->leveldetect0, gpio_bit,
trigger & IRQ_TYPE_LEVEL_LOW);
omap_gpio_rmw(base + bank->regs->leveldetect1, gpio_bit,
trigger & IRQ_TYPE_LEVEL_HIGH);
/*
* We need the edge detection enabled for to allow the GPIO block
* to be woken from idle state. Set the appropriate edge detection
* in addition to the level detection.
*/
omap_gpio_rmw(base + bank->regs->risingdetect, gpio_bit,
trigger & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH));
omap_gpio_rmw(base + bank->regs->fallingdetect, gpio_bit,
trigger & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW));
bank->context.leveldetect0 =
readl_relaxed(bank->base + bank->regs->leveldetect0);
bank->context.leveldetect1 =
readl_relaxed(bank->base + bank->regs->leveldetect1);
bank->context.risingdetect =
readl_relaxed(bank->base + bank->regs->risingdetect);
bank->context.fallingdetect =
readl_relaxed(bank->base + bank->regs->fallingdetect);
bank->level_mask = bank->context.leveldetect0 |
bank->context.leveldetect1;
/* This part needs to be executed always for OMAP{34xx, 44xx} */
if (!bank->regs->irqctrl && !omap_gpio_is_off_wakeup_capable(bank, gpio)) {
/*
* Log the edge gpio and manually trigger the IRQ
* after resume if the input level changes
* to avoid irq lost during PER RET/OFF mode
* Applies for omap2 non-wakeup gpio and all omap3 gpios
*/
if (trigger & IRQ_TYPE_EDGE_BOTH)
bank->enabled_non_wakeup_gpios |= gpio_bit;
else
bank->enabled_non_wakeup_gpios &= ~gpio_bit;
}
}
omap: gpio: Simultaneously requested rising and falling edge Some chips, namely any OMAP1 chips using METHOD_MPUIO, OMAP15xx and OMAP7xx, cannot be setup to respond to on-chip GPIO interrupts in both rising and falling edge directions -- they can only respond to one direction or the other, depending on how the ICR is configured. Additionally, current code forces rising edge detection if both flags are specified: if (trigger & IRQ_TYPE_EDGE_RISING) l |= 1 << gpio; else if (trigger & IRQ_TYPE_EDGE_FALLING) l &= ~(1 << gpio); else goto bad; This change implements a toggle function that will modify the ICR to flip the direction of interrupt for IRQs that are requested with both rising and falling flags. The toggle function is not called for chips and GPIOs it does not apply to through the use of a flip_mask that's added on a per-bank basis. The mask is only set for those GPIOs where a toggle is necessary. Edge detection starts out the same as above with FALLING mode first. The toggle happens on EACH interrupt; without it, we have the following sequence of actions on GPIO transition: ICR GPIO Result 0x1 0 -> 1 (rising) Interrupt 0x1 1 -> 0 (falling) No interrupt (set ICR to 0x0 manually) 0x0 0 -> 1 (rising) No interrupt 0x0 1 -> 0 (falling) Interrupt That is, with the ICR set to 1 for a gpio, only rising edge interrupts are caught, and with it set to 0, only falling edge interrupts are caught. If we add in the toggle, we get this: ICR GPIO Result 0x1 0 -> 1 (rising) Interrupt (ICR set to 0x0) 0x0 1 -> 0 (falling) Interrupt (ICR set to 0x1) 0x1 0 -> 1 ... so, both rising and falling are caught, per the request for both (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING). Signed-off-by: Cory Maccarrone <darkstar6262@gmail.com> Acked-by: Kevin Hilman <khilman@deeprootsystems.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-01-09 01:29:04 +07:00
/*
* This only applies to chips that can't do both rising and falling edge
* detection at once. For all other chips, this function is a noop.
*/
static void omap_toggle_gpio_edge_triggering(struct gpio_bank *bank, int gpio)
omap: gpio: Simultaneously requested rising and falling edge Some chips, namely any OMAP1 chips using METHOD_MPUIO, OMAP15xx and OMAP7xx, cannot be setup to respond to on-chip GPIO interrupts in both rising and falling edge directions -- they can only respond to one direction or the other, depending on how the ICR is configured. Additionally, current code forces rising edge detection if both flags are specified: if (trigger & IRQ_TYPE_EDGE_RISING) l |= 1 << gpio; else if (trigger & IRQ_TYPE_EDGE_FALLING) l &= ~(1 << gpio); else goto bad; This change implements a toggle function that will modify the ICR to flip the direction of interrupt for IRQs that are requested with both rising and falling flags. The toggle function is not called for chips and GPIOs it does not apply to through the use of a flip_mask that's added on a per-bank basis. The mask is only set for those GPIOs where a toggle is necessary. Edge detection starts out the same as above with FALLING mode first. The toggle happens on EACH interrupt; without it, we have the following sequence of actions on GPIO transition: ICR GPIO Result 0x1 0 -> 1 (rising) Interrupt 0x1 1 -> 0 (falling) No interrupt (set ICR to 0x0 manually) 0x0 0 -> 1 (rising) No interrupt 0x0 1 -> 0 (falling) Interrupt That is, with the ICR set to 1 for a gpio, only rising edge interrupts are caught, and with it set to 0, only falling edge interrupts are caught. If we add in the toggle, we get this: ICR GPIO Result 0x1 0 -> 1 (rising) Interrupt (ICR set to 0x0) 0x0 1 -> 0 (falling) Interrupt (ICR set to 0x1) 0x1 0 -> 1 ... so, both rising and falling are caught, per the request for both (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING). Signed-off-by: Cory Maccarrone <darkstar6262@gmail.com> Acked-by: Kevin Hilman <khilman@deeprootsystems.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-01-09 01:29:04 +07:00
{
if (IS_ENABLED(CONFIG_ARCH_OMAP1) && bank->regs->irqctrl) {
void __iomem *reg = bank->base + bank->regs->irqctrl;
writel_relaxed(readl_relaxed(reg) ^ BIT(gpio), reg);
}
omap: gpio: Simultaneously requested rising and falling edge Some chips, namely any OMAP1 chips using METHOD_MPUIO, OMAP15xx and OMAP7xx, cannot be setup to respond to on-chip GPIO interrupts in both rising and falling edge directions -- they can only respond to one direction or the other, depending on how the ICR is configured. Additionally, current code forces rising edge detection if both flags are specified: if (trigger & IRQ_TYPE_EDGE_RISING) l |= 1 << gpio; else if (trigger & IRQ_TYPE_EDGE_FALLING) l &= ~(1 << gpio); else goto bad; This change implements a toggle function that will modify the ICR to flip the direction of interrupt for IRQs that are requested with both rising and falling flags. The toggle function is not called for chips and GPIOs it does not apply to through the use of a flip_mask that's added on a per-bank basis. The mask is only set for those GPIOs where a toggle is necessary. Edge detection starts out the same as above with FALLING mode first. The toggle happens on EACH interrupt; without it, we have the following sequence of actions on GPIO transition: ICR GPIO Result 0x1 0 -> 1 (rising) Interrupt 0x1 1 -> 0 (falling) No interrupt (set ICR to 0x0 manually) 0x0 0 -> 1 (rising) No interrupt 0x0 1 -> 0 (falling) Interrupt That is, with the ICR set to 1 for a gpio, only rising edge interrupts are caught, and with it set to 0, only falling edge interrupts are caught. If we add in the toggle, we get this: ICR GPIO Result 0x1 0 -> 1 (rising) Interrupt (ICR set to 0x0) 0x0 1 -> 0 (falling) Interrupt (ICR set to 0x1) 0x1 0 -> 1 ... so, both rising and falling are caught, per the request for both (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING). Signed-off-by: Cory Maccarrone <darkstar6262@gmail.com> Acked-by: Kevin Hilman <khilman@deeprootsystems.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-01-09 01:29:04 +07:00
}
static int omap_set_gpio_triggering(struct gpio_bank *bank, int gpio,
unsigned trigger)
{
void __iomem *reg = bank->base;
u32 l = 0;
if (bank->regs->leveldetect0 && bank->regs->wkup_en) {
omap_set_gpio_trigger(bank, gpio, trigger);
} else if (bank->regs->irqctrl) {
reg += bank->regs->irqctrl;
l = readl_relaxed(reg);
if ((trigger & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH)
bank->toggle_mask |= BIT(gpio);
if (trigger & IRQ_TYPE_EDGE_RISING)
l |= BIT(gpio);
else if (trigger & IRQ_TYPE_EDGE_FALLING)
l &= ~(BIT(gpio));
else
return -EINVAL;
writel_relaxed(l, reg);
} else if (bank->regs->edgectrl1) {
if (gpio & 0x08)
reg += bank->regs->edgectrl2;
else
reg += bank->regs->edgectrl1;
gpio &= 0x07;
l = readl_relaxed(reg);
l &= ~(3 << (gpio << 1));
if (trigger & IRQ_TYPE_EDGE_RISING)
l |= 2 << (gpio << 1);
if (trigger & IRQ_TYPE_EDGE_FALLING)
l |= BIT(gpio << 1);
writel_relaxed(l, reg);
}
return 0;
}
static void omap_enable_gpio_module(struct gpio_bank *bank, unsigned offset)
{
if (bank->regs->pinctrl) {
void __iomem *reg = bank->base + bank->regs->pinctrl;
/* Claim the pin for MPU */
writel_relaxed(readl_relaxed(reg) | (BIT(offset)), reg);
}
if (bank->regs->ctrl && !BANK_USED(bank)) {
void __iomem *reg = bank->base + bank->regs->ctrl;
u32 ctrl;
ctrl = readl_relaxed(reg);
/* Module is enabled, clocks are not gated */
ctrl &= ~GPIO_MOD_CTRL_BIT;
writel_relaxed(ctrl, reg);
bank->context.ctrl = ctrl;
}
}
static void omap_disable_gpio_module(struct gpio_bank *bank, unsigned offset)
{
if (bank->regs->ctrl && !BANK_USED(bank)) {
void __iomem *reg = bank->base + bank->regs->ctrl;
u32 ctrl;
ctrl = readl_relaxed(reg);
/* Module is disabled, clocks are gated */
ctrl |= GPIO_MOD_CTRL_BIT;
writel_relaxed(ctrl, reg);
bank->context.ctrl = ctrl;
}
}
static int omap_gpio_is_input(struct gpio_bank *bank, unsigned offset)
{
void __iomem *reg = bank->base + bank->regs->direction;
return readl_relaxed(reg) & BIT(offset);
}
static void omap_gpio_init_irq(struct gpio_bank *bank, unsigned offset)
2015-01-17 05:50:50 +07:00
{
if (!LINE_USED(bank->mod_usage, offset)) {
omap_enable_gpio_module(bank, offset);
omap_set_gpio_direction(bank, offset, 1);
}
bank->irq_usage |= BIT(offset);
2015-01-17 05:50:50 +07:00
}
static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
{
struct gpio_bank *bank = omap_irq_data_get_bank(d);
int retval;
unsigned long flags;
unsigned offset = d->hwirq;
if (type & ~IRQ_TYPE_SENSE_MASK)
return -EINVAL;
if (!bank->regs->leveldetect0 &&
(type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH)))
return -EINVAL;
raw_spin_lock_irqsave(&bank->lock, flags);
retval = omap_set_gpio_triggering(bank, offset, type);
if (retval) {
raw_spin_unlock_irqrestore(&bank->lock, flags);
goto error;
}
omap_gpio_init_irq(bank, offset);
if (!omap_gpio_is_input(bank, offset)) {
raw_spin_unlock_irqrestore(&bank->lock, flags);
retval = -EINVAL;
goto error;
}
raw_spin_unlock_irqrestore(&bank->lock, flags);
if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
irq_set_handler_locked(d, handle_level_irq);
else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
/*
* Edge IRQs are already cleared/acked in irq_handler and
* not need to be masked, as result handle_edge_irq()
* logic is excessed here and may cause lose of interrupts.
* So just use handle_simple_irq.
*/
irq_set_handler_locked(d, handle_simple_irq);
return 0;
error:
return retval;
}
static void omap_clear_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
{
void __iomem *reg = bank->base;
reg += bank->regs->irqstatus;
writel_relaxed(gpio_mask, reg);
/* Workaround for clearing DSP GPIO interrupts to allow retention */
if (bank->regs->irqstatus2) {
reg = bank->base + bank->regs->irqstatus2;
writel_relaxed(gpio_mask, reg);
}
/* Flush posted write for the irq status to avoid spurious interrupts */
readl_relaxed(reg);
}
static inline void omap_clear_gpio_irqstatus(struct gpio_bank *bank,
unsigned offset)
{
omap_clear_gpio_irqbank(bank, BIT(offset));
}
static u32 omap_get_gpio_irqbank_mask(struct gpio_bank *bank)
{
void __iomem *reg = bank->base;
u32 l;
u32 mask = (BIT(bank->width)) - 1;
reg += bank->regs->irqenable;
l = readl_relaxed(reg);
if (bank->regs->irqenable_inv)
l = ~l;
l &= mask;
return l;
}
static inline void omap_set_gpio_irqenable(struct gpio_bank *bank,
unsigned offset, int enable)
{
void __iomem *reg = bank->base;
u32 gpio_mask = BIT(offset);
if (bank->regs->set_irqenable && bank->regs->clr_irqenable) {
if (enable) {
reg += bank->regs->set_irqenable;
bank->context.irqenable1 |= gpio_mask;
} else {
reg += bank->regs->clr_irqenable;
bank->context.irqenable1 &= ~gpio_mask;
}
writel_relaxed(gpio_mask, reg);
} else {
bank->context.irqenable1 =
omap_gpio_rmw(reg + bank->regs->irqenable, gpio_mask,
enable ^ bank->regs->irqenable_inv);
}
/*
* Program GPIO wakeup along with IRQ enable to satisfy OMAP4430 TRM
* note requiring correlation between the IRQ enable registers and
* the wakeup registers. In any case, we want wakeup from idle
* enabled for the GPIOs which support this feature.
*/
if (bank->regs->wkup_en &&
(bank->regs->edgectrl1 || !(bank->non_wakeup_gpios & gpio_mask))) {
bank->context.wake_en =
omap_gpio_rmw(bank->base + bank->regs->wkup_en,
gpio_mask, enable);
}
}
/* Use disable_irq_wake() and enable_irq_wake() functions from drivers */
static int omap_gpio_wake_enable(struct irq_data *d, unsigned int enable)
{
struct gpio_bank *bank = omap_irq_data_get_bank(d);
gpio: omap: convert to use generic irq handler This patch converts TI OMAP GPIO driver to use generic irq handler instead of chained IRQ handler. This way OMAP GPIO driver will be compatible with RT kernel where it will be forced thread IRQ handler while in non-RT kernel it still will be executed in HW IRQ context. As part of this change the IRQ wakeup configuration is applied to GPIO Bank IRQ as it now will be under control of IRQ PM Core during suspend. There are also additional benefits: - on-RT kernel there will be no complains any more about PM runtime usage in atomic context "BUG: sleeping function called from invalid context"; - GPIO bank IRQs will appear in /proc/interrupts and its usage statistic will be visible; - GPIO bank IRQs could be configured through IRQ proc_fs interface and, as result, could be a part of IRQ balancing process if needed; - GPIO bank IRQs will be under control of IRQ PM Core during suspend to RAM. Disadvantage: - additional runtime overhed as call chain till omap_gpio_irq_handler() will be longer now - necessity to use wa_lock in omap_gpio_irq_handler() to W/A warning in handle_irq_event_percpu() WARNING: CPU: 1 PID: 35 at kernel/irq/handle.c:149 handle_irq_event_percpu+0x51c/0x638() This patch doesn't fully follows recommendations provided by Sebastian Andrzej Siewior [1], because It's required to go through and check all GPIO IRQ pin states as fast as possible and pass control to handle_level_irq or handle_edge_irq. handle_level_irq or handle_edge_irq will perform actions specific for IRQ triggering type and wakeup corresponding registered threaded IRQ handler (at least it's expected to be threaded). IRQs can be lost if handle_nested_irq() will be used, because excecution time of some pin specific GPIO IRQ handler can be very significant and require accessing ext. devices (I2C). Idea of such kind reworking was also discussed in [2]. [1] http://www.spinics.net/lists/linux-omap/msg120665.html [2] http://www.spinics.net/lists/linux-omap/msg119516.html Tested-by: Tony Lindgren <tony@atomide.com> Tested-by: Austin Schuh <austin@peloton-tech.com> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com> Acked-by: Santosh Shilimkar <ssantosh@kernel.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2015-09-26 02:28:03 +07:00
gpio: omap: fix irq triggering in smart-idle wakeup mode Now GPIO IRQ loss is observed on dra7-evm after suspend/resume cycle in the following case: extcon_usb1(id_irq) -> pcf8575.gpio1 -> omapgpio6.gpio11 -> gic the extcon_usb1 is wake up source and it enables IRQ wake up for id_irq by calling enable/disable_irq_wake() during suspend/resume which, in turn, causes execution of omap_gpio_wake_enable(). And omap_gpio_wake_enable() will set/clear corresponding bit in GPIO_IRQWAKEN_x register. omapgpio6 configuration after boot - wakeup is enabled for GPIO IRQs by default from omap_gpio_irq_type: GPIO_IRQSTATUS_SET_0 | 0x00000400 GPIO_IRQSTATUS_CLR_0 | 0x00000400 GPIO_IRQWAKEN_0 | 0x00000400 GPIO_RISINGDETECT | 0x00000000 GPIO_FALLINGDETECT | 0x00000400 omapgpio6 configuration after after suspend/resume cycle: GPIO_IRQSTATUS_SET_0 | 0x00000400 GPIO_IRQSTATUS_CLR_0 | 0x00000400 GPIO_IRQWAKEN_0 | 0x00000000 <--- GPIO_RISINGDETECT | 0x00000000 GPIO_FALLINGDETECT | 0x00000400 As result, system will start to lose interrupts from pcf8575 GPIO expander, because when OMAP GPIO IP is in smart-idle wakeup mode, there is no guarantee that transition(s) on input non wake up GPIO pin will trigger asynchronous wake-up request to PRCM and then IRQ generation. IRQ will be generated when GPIO is in active mode - for example, some time after accessing GPIO bank registers IRQs will be generated normally, but issue will happen again once PRCM will put GPIO in low power smart-idle wakeup mode. Note 1. Issue is not reproduced if debounce clk is enabled for GPIO bank. Note 2. Issue hardly reproducible if GPIO pins group contains both wakeup/non-wakeup gpios - for example, it will be hard to reproduce issue with pin2 if GPIO_IRQWAKEN_0=0x1 GPIO_IRQSTATUS_SET_0=0x3 GPIO_FALLINGDETECT = 0x3 (TRM "Power Saving by Grouping the Edge/Level Detection"). Note 3. There nothing common bitween System wake up and OMAP GPIO bank IP wake up logic - the last one defines how the GPIO bank ON-IDLE-ON transition will happen inside SoC under control of PRCM. Hence, fix the problem by removing omap_set_gpio_wakeup() function completely and so keeping always in sync GPIO IRQ mask/unmask (IRQSTATUS_SET) and wake up enable (GPIO_IRQWAKEN) bits; and adding IRQCHIP_MASK_ON_SUSPEND flag in OMAP GPIO irqchip. That way non wakeup GPIO IRQs will be properly masked/unmask by IRQ PM core during suspend/resume cycle. Cc: Roger Quadros <rogerq@ti.com> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com> Acked-by: Tony Lindgren <tony@atomide.com> Acked-by: Santosh Shilimkar <santosh.shilimkar@oracle.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2016-04-12 17:52:31 +07:00
return irq_set_irq_wake(bank->irq, enable);
}
/*
* We need to unmask the GPIO bank interrupt as soon as possible to
* avoid missing GPIO interrupts for other lines in the bank.
* Then we need to mask-read-clear-unmask the triggered GPIO lines
* in the bank to avoid missing nested interrupts for a GPIO line.
* If we wait to unmask individual GPIO lines in the bank after the
* line's interrupt handler has been run, we may miss some nested
* interrupts.
*/
gpio: omap: convert to use generic irq handler This patch converts TI OMAP GPIO driver to use generic irq handler instead of chained IRQ handler. This way OMAP GPIO driver will be compatible with RT kernel where it will be forced thread IRQ handler while in non-RT kernel it still will be executed in HW IRQ context. As part of this change the IRQ wakeup configuration is applied to GPIO Bank IRQ as it now will be under control of IRQ PM Core during suspend. There are also additional benefits: - on-RT kernel there will be no complains any more about PM runtime usage in atomic context "BUG: sleeping function called from invalid context"; - GPIO bank IRQs will appear in /proc/interrupts and its usage statistic will be visible; - GPIO bank IRQs could be configured through IRQ proc_fs interface and, as result, could be a part of IRQ balancing process if needed; - GPIO bank IRQs will be under control of IRQ PM Core during suspend to RAM. Disadvantage: - additional runtime overhed as call chain till omap_gpio_irq_handler() will be longer now - necessity to use wa_lock in omap_gpio_irq_handler() to W/A warning in handle_irq_event_percpu() WARNING: CPU: 1 PID: 35 at kernel/irq/handle.c:149 handle_irq_event_percpu+0x51c/0x638() This patch doesn't fully follows recommendations provided by Sebastian Andrzej Siewior [1], because It's required to go through and check all GPIO IRQ pin states as fast as possible and pass control to handle_level_irq or handle_edge_irq. handle_level_irq or handle_edge_irq will perform actions specific for IRQ triggering type and wakeup corresponding registered threaded IRQ handler (at least it's expected to be threaded). IRQs can be lost if handle_nested_irq() will be used, because excecution time of some pin specific GPIO IRQ handler can be very significant and require accessing ext. devices (I2C). Idea of such kind reworking was also discussed in [2]. [1] http://www.spinics.net/lists/linux-omap/msg120665.html [2] http://www.spinics.net/lists/linux-omap/msg119516.html Tested-by: Tony Lindgren <tony@atomide.com> Tested-by: Austin Schuh <austin@peloton-tech.com> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com> Acked-by: Santosh Shilimkar <ssantosh@kernel.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2015-09-26 02:28:03 +07:00
static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
{
void __iomem *isr_reg = NULL;
u32 enabled, isr, edge;
unsigned int bit;
gpio: omap: convert to use generic irq handler This patch converts TI OMAP GPIO driver to use generic irq handler instead of chained IRQ handler. This way OMAP GPIO driver will be compatible with RT kernel where it will be forced thread IRQ handler while in non-RT kernel it still will be executed in HW IRQ context. As part of this change the IRQ wakeup configuration is applied to GPIO Bank IRQ as it now will be under control of IRQ PM Core during suspend. There are also additional benefits: - on-RT kernel there will be no complains any more about PM runtime usage in atomic context "BUG: sleeping function called from invalid context"; - GPIO bank IRQs will appear in /proc/interrupts and its usage statistic will be visible; - GPIO bank IRQs could be configured through IRQ proc_fs interface and, as result, could be a part of IRQ balancing process if needed; - GPIO bank IRQs will be under control of IRQ PM Core during suspend to RAM. Disadvantage: - additional runtime overhed as call chain till omap_gpio_irq_handler() will be longer now - necessity to use wa_lock in omap_gpio_irq_handler() to W/A warning in handle_irq_event_percpu() WARNING: CPU: 1 PID: 35 at kernel/irq/handle.c:149 handle_irq_event_percpu+0x51c/0x638() This patch doesn't fully follows recommendations provided by Sebastian Andrzej Siewior [1], because It's required to go through and check all GPIO IRQ pin states as fast as possible and pass control to handle_level_irq or handle_edge_irq. handle_level_irq or handle_edge_irq will perform actions specific for IRQ triggering type and wakeup corresponding registered threaded IRQ handler (at least it's expected to be threaded). IRQs can be lost if handle_nested_irq() will be used, because excecution time of some pin specific GPIO IRQ handler can be very significant and require accessing ext. devices (I2C). Idea of such kind reworking was also discussed in [2]. [1] http://www.spinics.net/lists/linux-omap/msg120665.html [2] http://www.spinics.net/lists/linux-omap/msg119516.html Tested-by: Tony Lindgren <tony@atomide.com> Tested-by: Austin Schuh <austin@peloton-tech.com> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com> Acked-by: Santosh Shilimkar <ssantosh@kernel.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2015-09-26 02:28:03 +07:00
struct gpio_bank *bank = gpiobank;
unsigned long wa_lock_flags;
unsigned long lock_flags;
isr_reg = bank->base + bank->regs->irqstatus;
if (WARN_ON(!isr_reg))
goto exit;
if (WARN_ONCE(!pm_runtime_active(bank->chip.parent),
"gpio irq%i while runtime suspended?\n", irq))
return IRQ_NONE;
gpio: omap: convert to use generic irq handler This patch converts TI OMAP GPIO driver to use generic irq handler instead of chained IRQ handler. This way OMAP GPIO driver will be compatible with RT kernel where it will be forced thread IRQ handler while in non-RT kernel it still will be executed in HW IRQ context. As part of this change the IRQ wakeup configuration is applied to GPIO Bank IRQ as it now will be under control of IRQ PM Core during suspend. There are also additional benefits: - on-RT kernel there will be no complains any more about PM runtime usage in atomic context "BUG: sleeping function called from invalid context"; - GPIO bank IRQs will appear in /proc/interrupts and its usage statistic will be visible; - GPIO bank IRQs could be configured through IRQ proc_fs interface and, as result, could be a part of IRQ balancing process if needed; - GPIO bank IRQs will be under control of IRQ PM Core during suspend to RAM. Disadvantage: - additional runtime overhed as call chain till omap_gpio_irq_handler() will be longer now - necessity to use wa_lock in omap_gpio_irq_handler() to W/A warning in handle_irq_event_percpu() WARNING: CPU: 1 PID: 35 at kernel/irq/handle.c:149 handle_irq_event_percpu+0x51c/0x638() This patch doesn't fully follows recommendations provided by Sebastian Andrzej Siewior [1], because It's required to go through and check all GPIO IRQ pin states as fast as possible and pass control to handle_level_irq or handle_edge_irq. handle_level_irq or handle_edge_irq will perform actions specific for IRQ triggering type and wakeup corresponding registered threaded IRQ handler (at least it's expected to be threaded). IRQs can be lost if handle_nested_irq() will be used, because excecution time of some pin specific GPIO IRQ handler can be very significant and require accessing ext. devices (I2C). Idea of such kind reworking was also discussed in [2]. [1] http://www.spinics.net/lists/linux-omap/msg120665.html [2] http://www.spinics.net/lists/linux-omap/msg119516.html Tested-by: Tony Lindgren <tony@atomide.com> Tested-by: Austin Schuh <austin@peloton-tech.com> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com> Acked-by: Santosh Shilimkar <ssantosh@kernel.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2015-09-26 02:28:03 +07:00
while (1) {
raw_spin_lock_irqsave(&bank->lock, lock_flags);
enabled = omap_get_gpio_irqbank_mask(bank);
isr = readl_relaxed(isr_reg) & enabled;
/*
* Clear edge sensitive interrupts before calling handler(s)
* so subsequent edge transitions are not missed while the
* handlers are running.
*/
edge = isr & ~bank->level_mask;
if (edge)
omap_clear_gpio_irqbank(bank, edge);
raw_spin_unlock_irqrestore(&bank->lock, lock_flags);
if (!isr)
break;
while (isr) {
bit = __ffs(isr);
isr &= ~(BIT(bit));
raw_spin_lock_irqsave(&bank->lock, lock_flags);
omap: gpio: Simultaneously requested rising and falling edge Some chips, namely any OMAP1 chips using METHOD_MPUIO, OMAP15xx and OMAP7xx, cannot be setup to respond to on-chip GPIO interrupts in both rising and falling edge directions -- they can only respond to one direction or the other, depending on how the ICR is configured. Additionally, current code forces rising edge detection if both flags are specified: if (trigger & IRQ_TYPE_EDGE_RISING) l |= 1 << gpio; else if (trigger & IRQ_TYPE_EDGE_FALLING) l &= ~(1 << gpio); else goto bad; This change implements a toggle function that will modify the ICR to flip the direction of interrupt for IRQs that are requested with both rising and falling flags. The toggle function is not called for chips and GPIOs it does not apply to through the use of a flip_mask that's added on a per-bank basis. The mask is only set for those GPIOs where a toggle is necessary. Edge detection starts out the same as above with FALLING mode first. The toggle happens on EACH interrupt; without it, we have the following sequence of actions on GPIO transition: ICR GPIO Result 0x1 0 -> 1 (rising) Interrupt 0x1 1 -> 0 (falling) No interrupt (set ICR to 0x0 manually) 0x0 0 -> 1 (rising) No interrupt 0x0 1 -> 0 (falling) Interrupt That is, with the ICR set to 1 for a gpio, only rising edge interrupts are caught, and with it set to 0, only falling edge interrupts are caught. If we add in the toggle, we get this: ICR GPIO Result 0x1 0 -> 1 (rising) Interrupt (ICR set to 0x0) 0x0 1 -> 0 (falling) Interrupt (ICR set to 0x1) 0x1 0 -> 1 ... so, both rising and falling are caught, per the request for both (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING). Signed-off-by: Cory Maccarrone <darkstar6262@gmail.com> Acked-by: Kevin Hilman <khilman@deeprootsystems.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-01-09 01:29:04 +07:00
/*
* Some chips can't respond to both rising and falling
* at the same time. If this irq was requested with
* both flags, we need to flip the ICR data for the IRQ
* to respond to the IRQ for the opposite direction.
* This will be indicated in the bank toggle_mask.
*/
if (bank->toggle_mask & (BIT(bit)))
omap_toggle_gpio_edge_triggering(bank, bit);
omap: gpio: Simultaneously requested rising and falling edge Some chips, namely any OMAP1 chips using METHOD_MPUIO, OMAP15xx and OMAP7xx, cannot be setup to respond to on-chip GPIO interrupts in both rising and falling edge directions -- they can only respond to one direction or the other, depending on how the ICR is configured. Additionally, current code forces rising edge detection if both flags are specified: if (trigger & IRQ_TYPE_EDGE_RISING) l |= 1 << gpio; else if (trigger & IRQ_TYPE_EDGE_FALLING) l &= ~(1 << gpio); else goto bad; This change implements a toggle function that will modify the ICR to flip the direction of interrupt for IRQs that are requested with both rising and falling flags. The toggle function is not called for chips and GPIOs it does not apply to through the use of a flip_mask that's added on a per-bank basis. The mask is only set for those GPIOs where a toggle is necessary. Edge detection starts out the same as above with FALLING mode first. The toggle happens on EACH interrupt; without it, we have the following sequence of actions on GPIO transition: ICR GPIO Result 0x1 0 -> 1 (rising) Interrupt 0x1 1 -> 0 (falling) No interrupt (set ICR to 0x0 manually) 0x0 0 -> 1 (rising) No interrupt 0x0 1 -> 0 (falling) Interrupt That is, with the ICR set to 1 for a gpio, only rising edge interrupts are caught, and with it set to 0, only falling edge interrupts are caught. If we add in the toggle, we get this: ICR GPIO Result 0x1 0 -> 1 (rising) Interrupt (ICR set to 0x0) 0x0 1 -> 0 (falling) Interrupt (ICR set to 0x1) 0x1 0 -> 1 ... so, both rising and falling are caught, per the request for both (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING). Signed-off-by: Cory Maccarrone <darkstar6262@gmail.com> Acked-by: Kevin Hilman <khilman@deeprootsystems.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-01-09 01:29:04 +07:00
raw_spin_unlock_irqrestore(&bank->lock, lock_flags);
gpio: omap: convert to use generic irq handler This patch converts TI OMAP GPIO driver to use generic irq handler instead of chained IRQ handler. This way OMAP GPIO driver will be compatible with RT kernel where it will be forced thread IRQ handler while in non-RT kernel it still will be executed in HW IRQ context. As part of this change the IRQ wakeup configuration is applied to GPIO Bank IRQ as it now will be under control of IRQ PM Core during suspend. There are also additional benefits: - on-RT kernel there will be no complains any more about PM runtime usage in atomic context "BUG: sleeping function called from invalid context"; - GPIO bank IRQs will appear in /proc/interrupts and its usage statistic will be visible; - GPIO bank IRQs could be configured through IRQ proc_fs interface and, as result, could be a part of IRQ balancing process if needed; - GPIO bank IRQs will be under control of IRQ PM Core during suspend to RAM. Disadvantage: - additional runtime overhed as call chain till omap_gpio_irq_handler() will be longer now - necessity to use wa_lock in omap_gpio_irq_handler() to W/A warning in handle_irq_event_percpu() WARNING: CPU: 1 PID: 35 at kernel/irq/handle.c:149 handle_irq_event_percpu+0x51c/0x638() This patch doesn't fully follows recommendations provided by Sebastian Andrzej Siewior [1], because It's required to go through and check all GPIO IRQ pin states as fast as possible and pass control to handle_level_irq or handle_edge_irq. handle_level_irq or handle_edge_irq will perform actions specific for IRQ triggering type and wakeup corresponding registered threaded IRQ handler (at least it's expected to be threaded). IRQs can be lost if handle_nested_irq() will be used, because excecution time of some pin specific GPIO IRQ handler can be very significant and require accessing ext. devices (I2C). Idea of such kind reworking was also discussed in [2]. [1] http://www.spinics.net/lists/linux-omap/msg120665.html [2] http://www.spinics.net/lists/linux-omap/msg119516.html Tested-by: Tony Lindgren <tony@atomide.com> Tested-by: Austin Schuh <austin@peloton-tech.com> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com> Acked-by: Santosh Shilimkar <ssantosh@kernel.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2015-09-26 02:28:03 +07:00
raw_spin_lock_irqsave(&bank->wa_lock, wa_lock_flags);
generic_handle_irq(irq_find_mapping(bank->chip.irq.domain,
bit));
gpio: omap: convert to use generic irq handler This patch converts TI OMAP GPIO driver to use generic irq handler instead of chained IRQ handler. This way OMAP GPIO driver will be compatible with RT kernel where it will be forced thread IRQ handler while in non-RT kernel it still will be executed in HW IRQ context. As part of this change the IRQ wakeup configuration is applied to GPIO Bank IRQ as it now will be under control of IRQ PM Core during suspend. There are also additional benefits: - on-RT kernel there will be no complains any more about PM runtime usage in atomic context "BUG: sleeping function called from invalid context"; - GPIO bank IRQs will appear in /proc/interrupts and its usage statistic will be visible; - GPIO bank IRQs could be configured through IRQ proc_fs interface and, as result, could be a part of IRQ balancing process if needed; - GPIO bank IRQs will be under control of IRQ PM Core during suspend to RAM. Disadvantage: - additional runtime overhed as call chain till omap_gpio_irq_handler() will be longer now - necessity to use wa_lock in omap_gpio_irq_handler() to W/A warning in handle_irq_event_percpu() WARNING: CPU: 1 PID: 35 at kernel/irq/handle.c:149 handle_irq_event_percpu+0x51c/0x638() This patch doesn't fully follows recommendations provided by Sebastian Andrzej Siewior [1], because It's required to go through and check all GPIO IRQ pin states as fast as possible and pass control to handle_level_irq or handle_edge_irq. handle_level_irq or handle_edge_irq will perform actions specific for IRQ triggering type and wakeup corresponding registered threaded IRQ handler (at least it's expected to be threaded). IRQs can be lost if handle_nested_irq() will be used, because excecution time of some pin specific GPIO IRQ handler can be very significant and require accessing ext. devices (I2C). Idea of such kind reworking was also discussed in [2]. [1] http://www.spinics.net/lists/linux-omap/msg120665.html [2] http://www.spinics.net/lists/linux-omap/msg119516.html Tested-by: Tony Lindgren <tony@atomide.com> Tested-by: Austin Schuh <austin@peloton-tech.com> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com> Acked-by: Santosh Shilimkar <ssantosh@kernel.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2015-09-26 02:28:03 +07:00
raw_spin_unlock_irqrestore(&bank->wa_lock,
wa_lock_flags);
}
}
exit:
gpio: omap: convert to use generic irq handler This patch converts TI OMAP GPIO driver to use generic irq handler instead of chained IRQ handler. This way OMAP GPIO driver will be compatible with RT kernel where it will be forced thread IRQ handler while in non-RT kernel it still will be executed in HW IRQ context. As part of this change the IRQ wakeup configuration is applied to GPIO Bank IRQ as it now will be under control of IRQ PM Core during suspend. There are also additional benefits: - on-RT kernel there will be no complains any more about PM runtime usage in atomic context "BUG: sleeping function called from invalid context"; - GPIO bank IRQs will appear in /proc/interrupts and its usage statistic will be visible; - GPIO bank IRQs could be configured through IRQ proc_fs interface and, as result, could be a part of IRQ balancing process if needed; - GPIO bank IRQs will be under control of IRQ PM Core during suspend to RAM. Disadvantage: - additional runtime overhed as call chain till omap_gpio_irq_handler() will be longer now - necessity to use wa_lock in omap_gpio_irq_handler() to W/A warning in handle_irq_event_percpu() WARNING: CPU: 1 PID: 35 at kernel/irq/handle.c:149 handle_irq_event_percpu+0x51c/0x638() This patch doesn't fully follows recommendations provided by Sebastian Andrzej Siewior [1], because It's required to go through and check all GPIO IRQ pin states as fast as possible and pass control to handle_level_irq or handle_edge_irq. handle_level_irq or handle_edge_irq will perform actions specific for IRQ triggering type and wakeup corresponding registered threaded IRQ handler (at least it's expected to be threaded). IRQs can be lost if handle_nested_irq() will be used, because excecution time of some pin specific GPIO IRQ handler can be very significant and require accessing ext. devices (I2C). Idea of such kind reworking was also discussed in [2]. [1] http://www.spinics.net/lists/linux-omap/msg120665.html [2] http://www.spinics.net/lists/linux-omap/msg119516.html Tested-by: Tony Lindgren <tony@atomide.com> Tested-by: Austin Schuh <austin@peloton-tech.com> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com> Acked-by: Santosh Shilimkar <ssantosh@kernel.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2015-09-26 02:28:03 +07:00
return IRQ_HANDLED;
}
2015-01-17 05:50:50 +07:00
static unsigned int omap_gpio_irq_startup(struct irq_data *d)
{
struct gpio_bank *bank = omap_irq_data_get_bank(d);
unsigned long flags;
unsigned offset = d->hwirq;
2015-01-17 05:50:50 +07:00
raw_spin_lock_irqsave(&bank->lock, flags);
if (!LINE_USED(bank->mod_usage, offset))
omap_set_gpio_direction(bank, offset, 1);
omap_enable_gpio_module(bank, offset);
bank->irq_usage |= BIT(offset);
raw_spin_unlock_irqrestore(&bank->lock, flags);
2015-01-17 05:50:50 +07:00
omap_gpio_unmask_irq(d);
return 0;
}
static void omap_gpio_irq_shutdown(struct irq_data *d)
{
struct gpio_bank *bank = omap_irq_data_get_bank(d);
unsigned long flags;
unsigned offset = d->hwirq;
raw_spin_lock_irqsave(&bank->lock, flags);
bank->irq_usage &= ~(BIT(offset));
omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
omap_clear_gpio_irqstatus(bank, offset);
omap_set_gpio_irqenable(bank, offset, 0);
if (!LINE_USED(bank->mod_usage, offset))
omap_clear_gpio_debounce(bank, offset);
omap_disable_gpio_module(bank, offset);
raw_spin_unlock_irqrestore(&bank->lock, flags);
gpio: omap: move pm runtime in irq_chip.irq_bus_lock/sync_unlock The PM runtime API can't be used in atomic contex on -RT even if it's configured as irqsafe. As result, below error report can be seen when PM runtime API called from IRQ chip's callbacks irq_startup/irq_shutdown/irq_set_type, because they are protected by RAW spinlock: BUG: sleeping function called from invalid context at kernel/locking/rtmutex.c:917 in_atomic(): 1, irqs_disabled(): 128, pid: 96, name: insmod 3 locks held by insmod/96: #0: (&dev->mutex){......}, at: [<c04752c8>] __driver_attach+0x54/0xa0 #1: (&dev->mutex){......}, at: [<c04752d4>] __driver_attach+0x60/0xa0 #2: (class){......}, at: [<c00a408c>] __irq_get_desc_lock+0x60/0xa4 irq event stamp: 1834 hardirqs last enabled at (1833): [<c06ab2a4>] _raw_spin_unlock_irqrestore+0x88/0x90 hardirqs last disabled at (1834): [<c06ab068>] _raw_spin_lock_irqsave+0x2c/0x64 softirqs last enabled at (0): [<c003d220>] copy_process.part.52+0x410/0x19d8 softirqs last disabled at (0): [< (null)>] (null) Preemption disabled at:[< (null)>] (null) CPU: 1 PID: 96 Comm: insmod Tainted: G W O 4.1.3-rt3-00618-g57e2387-dirty #184 Hardware name: Generic DRA74X (Flattened Device Tree) [<c00190f4>] (unwind_backtrace) from [<c0014734>] (show_stack+0x20/0x24) [<c0014734>] (show_stack) from [<c06a62ec>] (dump_stack+0x88/0xdc) [<c06a62ec>] (dump_stack) from [<c006ca44>] (___might_sleep+0x198/0x2a8) [<c006ca44>] (___might_sleep) from [<c06ab6d4>] (rt_spin_lock+0x30/0x70) [<c06ab6d4>] (rt_spin_lock) from [<c04815ac>] (__pm_runtime_resume+0x68/0xa4) [<c04815ac>] (__pm_runtime_resume) from [<c04123f4>] (omap_gpio_irq_type+0x188/0x1d8) [<c04123f4>] (omap_gpio_irq_type) from [<c00a64e4>] (__irq_set_trigger+0x68/0x130) [<c00a64e4>] (__irq_set_trigger) from [<c00a7bc4>] (irq_set_irq_type+0x44/0x6c) [<c00a7bc4>] (irq_set_irq_type) from [<c00abbf8>] (irq_create_of_mapping+0x120/0x174) [<c00abbf8>] (irq_create_of_mapping) from [<c0577b74>] (of_irq_get+0x48/0x58) [<c0577b74>] (of_irq_get) from [<c0540a14>] (i2c_device_probe+0x54/0x15c) [<c0540a14>] (i2c_device_probe) from [<c04750dc>] (driver_probe_device+0x184/0x2c8) [<c04750dc>] (driver_probe_device) from [<c0475310>] (__driver_attach+0x9c/0xa0) [<c0475310>] (__driver_attach) from [<c0473238>] (bus_for_each_dev+0x7c/0xb0) [<c0473238>] (bus_for_each_dev) from [<c0474af4>] (driver_attach+0x28/0x30) [<c0474af4>] (driver_attach) from [<c0474760>] (bus_add_driver+0x154/0x200) [<c0474760>] (bus_add_driver) from [<c0476348>] (driver_register+0x88/0x108) [<c0476348>] (driver_register) from [<c0541600>] (i2c_register_driver+0x3c/0x90) [<c0541600>] (i2c_register_driver) from [<bf003018>] (pcf857x_init+0x18/0x24 [gpio_pcf857x]) [<bf003018>] (pcf857x_init [gpio_pcf857x]) from [<c000998c>] (do_one_initcall+0x128/0x1e8) [<c000998c>] (do_one_initcall) from [<c06a4220>] (do_init_module+0x6c/0x1bc) [<c06a4220>] (do_init_module) from [<c00dd0c8>] (load_module+0x18e8/0x21c4) [<c00dd0c8>] (load_module) from [<c00ddaa0>] (SyS_init_module+0xfc/0x158) [<c00ddaa0>] (SyS_init_module) from [<c000ff40>] (ret_fast_syscall+0x0/0x54) The IRQ chip interface defines only two callbacks which are executed in non-atomic contex - irq_bus_lock/irq_bus_sync_unlock, so lets move PM runtime calls there. Tested-by: Tony Lindgren <tony@atomide.com> Tested-by: Austin Schuh <austin@peloton-tech.com> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com> Acked-by: Santosh Shilimkar <ssantosh@kernel.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2015-09-26 02:28:02 +07:00
}
static void omap_gpio_irq_bus_lock(struct irq_data *data)
{
struct gpio_bank *bank = omap_irq_data_get_bank(data);
pm_runtime_get_sync(bank->chip.parent);
gpio: omap: move pm runtime in irq_chip.irq_bus_lock/sync_unlock The PM runtime API can't be used in atomic contex on -RT even if it's configured as irqsafe. As result, below error report can be seen when PM runtime API called from IRQ chip's callbacks irq_startup/irq_shutdown/irq_set_type, because they are protected by RAW spinlock: BUG: sleeping function called from invalid context at kernel/locking/rtmutex.c:917 in_atomic(): 1, irqs_disabled(): 128, pid: 96, name: insmod 3 locks held by insmod/96: #0: (&dev->mutex){......}, at: [<c04752c8>] __driver_attach+0x54/0xa0 #1: (&dev->mutex){......}, at: [<c04752d4>] __driver_attach+0x60/0xa0 #2: (class){......}, at: [<c00a408c>] __irq_get_desc_lock+0x60/0xa4 irq event stamp: 1834 hardirqs last enabled at (1833): [<c06ab2a4>] _raw_spin_unlock_irqrestore+0x88/0x90 hardirqs last disabled at (1834): [<c06ab068>] _raw_spin_lock_irqsave+0x2c/0x64 softirqs last enabled at (0): [<c003d220>] copy_process.part.52+0x410/0x19d8 softirqs last disabled at (0): [< (null)>] (null) Preemption disabled at:[< (null)>] (null) CPU: 1 PID: 96 Comm: insmod Tainted: G W O 4.1.3-rt3-00618-g57e2387-dirty #184 Hardware name: Generic DRA74X (Flattened Device Tree) [<c00190f4>] (unwind_backtrace) from [<c0014734>] (show_stack+0x20/0x24) [<c0014734>] (show_stack) from [<c06a62ec>] (dump_stack+0x88/0xdc) [<c06a62ec>] (dump_stack) from [<c006ca44>] (___might_sleep+0x198/0x2a8) [<c006ca44>] (___might_sleep) from [<c06ab6d4>] (rt_spin_lock+0x30/0x70) [<c06ab6d4>] (rt_spin_lock) from [<c04815ac>] (__pm_runtime_resume+0x68/0xa4) [<c04815ac>] (__pm_runtime_resume) from [<c04123f4>] (omap_gpio_irq_type+0x188/0x1d8) [<c04123f4>] (omap_gpio_irq_type) from [<c00a64e4>] (__irq_set_trigger+0x68/0x130) [<c00a64e4>] (__irq_set_trigger) from [<c00a7bc4>] (irq_set_irq_type+0x44/0x6c) [<c00a7bc4>] (irq_set_irq_type) from [<c00abbf8>] (irq_create_of_mapping+0x120/0x174) [<c00abbf8>] (irq_create_of_mapping) from [<c0577b74>] (of_irq_get+0x48/0x58) [<c0577b74>] (of_irq_get) from [<c0540a14>] (i2c_device_probe+0x54/0x15c) [<c0540a14>] (i2c_device_probe) from [<c04750dc>] (driver_probe_device+0x184/0x2c8) [<c04750dc>] (driver_probe_device) from [<c0475310>] (__driver_attach+0x9c/0xa0) [<c0475310>] (__driver_attach) from [<c0473238>] (bus_for_each_dev+0x7c/0xb0) [<c0473238>] (bus_for_each_dev) from [<c0474af4>] (driver_attach+0x28/0x30) [<c0474af4>] (driver_attach) from [<c0474760>] (bus_add_driver+0x154/0x200) [<c0474760>] (bus_add_driver) from [<c0476348>] (driver_register+0x88/0x108) [<c0476348>] (driver_register) from [<c0541600>] (i2c_register_driver+0x3c/0x90) [<c0541600>] (i2c_register_driver) from [<bf003018>] (pcf857x_init+0x18/0x24 [gpio_pcf857x]) [<bf003018>] (pcf857x_init [gpio_pcf857x]) from [<c000998c>] (do_one_initcall+0x128/0x1e8) [<c000998c>] (do_one_initcall) from [<c06a4220>] (do_init_module+0x6c/0x1bc) [<c06a4220>] (do_init_module) from [<c00dd0c8>] (load_module+0x18e8/0x21c4) [<c00dd0c8>] (load_module) from [<c00ddaa0>] (SyS_init_module+0xfc/0x158) [<c00ddaa0>] (SyS_init_module) from [<c000ff40>] (ret_fast_syscall+0x0/0x54) The IRQ chip interface defines only two callbacks which are executed in non-atomic contex - irq_bus_lock/irq_bus_sync_unlock, so lets move PM runtime calls there. Tested-by: Tony Lindgren <tony@atomide.com> Tested-by: Austin Schuh <austin@peloton-tech.com> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com> Acked-by: Santosh Shilimkar <ssantosh@kernel.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2015-09-26 02:28:02 +07:00
}
static void gpio_irq_bus_sync_unlock(struct irq_data *data)
{
struct gpio_bank *bank = omap_irq_data_get_bank(data);
pm_runtime_put(bank->chip.parent);
}
static void omap_gpio_mask_irq(struct irq_data *d)
{
struct gpio_bank *bank = omap_irq_data_get_bank(d);
unsigned offset = d->hwirq;
unsigned long flags;
raw_spin_lock_irqsave(&bank->lock, flags);
omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
omap_set_gpio_irqenable(bank, offset, 0);
raw_spin_unlock_irqrestore(&bank->lock, flags);
}
static void omap_gpio_unmask_irq(struct irq_data *d)
{
struct gpio_bank *bank = omap_irq_data_get_bank(d);
unsigned offset = d->hwirq;
u32 trigger = irqd_get_trigger_type(d);
unsigned long flags;
raw_spin_lock_irqsave(&bank->lock, flags);
gpio: gpio-omap: fix level interrupt idling Tony notes that the GPIO module does not idle when level interrupts are in use, as the wakeup appears to get stuck. After extensive investigation, it appears that the wakeup will only be cleared if the interrupt status register is cleared while the interrupt is enabled. However, we are currently clearing it with the interrupt disabled for level-based interrupts. It is acknowledged that this observed behaviour conflicts with a statement in the TRM: CAUTION After servicing the interrupt, the status bit in the interrupt status register (GPIOi.GPIO_IRQSTATUS_0 or GPIOi.GPIO_IRQSTATUS_1) must be reset and the interrupt line released (by setting the corresponding bit of the interrupt status register to 1) before enabling an interrupt for the GPIO channel in the interrupt-enable register (GPIOi.GPIO_IRQSTATUS_SET_0 or GPIOi.GPIO_IRQSTATUS_SET_1) to prevent the occurrence of unexpected interrupts when enabling an interrupt for the GPIO channel. However, this does not appear to be a practical problem. Further, as reported by Grygorii Strashko <grygorii.strashko@ti.com>, the TI Android kernel tree has an earlier similar patch as "GPIO: OMAP: Fix the sequence to clear the IRQ status" saying: if the status is cleared after disabling the IRQ then sWAKEUP will not be cleared and gates the module transition When we unmask the level interrupt after the interrupt has been handled, enable the interrupt and only then clear the interrupt. If the interrupt is still pending, the hardware will re-assert the interrupt status. Should the caution note in the TRM prove to be a problem, we could use a clear-enable-clear sequence instead. Cc: Aaro Koskinen <aaro.koskinen@iki.fi> Cc: Keerthy <j-keerthy@ti.com> Cc: Peter Ujfalusi <peter.ujfalusi@ti.com> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> [tony@atomide.com: updated comments based on an earlier TI patch] Signed-off-by: Tony Lindgren <tony@atomide.com> Acked-by: Grygorii Strashko <grygorii.strashko@ti.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2019-03-02 02:02:52 +07:00
omap_set_gpio_irqenable(bank, offset, 1);
/*
* For level-triggered GPIOs, clearing must be done after the source
* is cleared, thus after the handler has run. OMAP4 needs this done
* after enabing the interrupt to clear the wakeup status.
*/
if (bank->regs->leveldetect0 && bank->regs->wkup_en &&
trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
omap_clear_gpio_irqstatus(bank, offset);
if (trigger)
omap_set_gpio_triggering(bank, offset, trigger);
raw_spin_unlock_irqrestore(&bank->lock, flags);
}
/*---------------------------------------------------------------------*/
static int omap_mpuio_suspend_noirq(struct device *dev)
{
struct gpio_bank *bank = dev_get_drvdata(dev);
void __iomem *mask_reg = bank->base +
OMAP_MPUIO_GPIO_MASKIT / bank->stride;
unsigned long flags;
raw_spin_lock_irqsave(&bank->lock, flags);
writel_relaxed(0xffff & ~bank->context.wake_en, mask_reg);
raw_spin_unlock_irqrestore(&bank->lock, flags);
return 0;
}
static int omap_mpuio_resume_noirq(struct device *dev)
{
struct gpio_bank *bank = dev_get_drvdata(dev);
void __iomem *mask_reg = bank->base +
OMAP_MPUIO_GPIO_MASKIT / bank->stride;
unsigned long flags;
raw_spin_lock_irqsave(&bank->lock, flags);
writel_relaxed(bank->context.wake_en, mask_reg);
raw_spin_unlock_irqrestore(&bank->lock, flags);
return 0;
}
static const struct dev_pm_ops omap_mpuio_dev_pm_ops = {
.suspend_noirq = omap_mpuio_suspend_noirq,
.resume_noirq = omap_mpuio_resume_noirq,
};
/* use platform_driver for this. */
static struct platform_driver omap_mpuio_driver = {
.driver = {
.name = "mpuio",
.pm = &omap_mpuio_dev_pm_ops,
},
};
static struct platform_device omap_mpuio_device = {
.name = "mpuio",
.id = -1,
.dev = {
.driver = &omap_mpuio_driver.driver,
}
/* could list the /proc/iomem resources */
};
static inline void omap_mpuio_init(struct gpio_bank *bank)
{
OMAP: GPIO: Implement GPIO as a platform device Implement GPIO as a platform device. GPIO APIs are used in machine_init functions. Hence it is required to complete GPIO probe before board_init. Therefore GPIO device register and driver register are implemented as postcore_initcalls. omap_gpio_init() does nothing now and this function would be removed in the next patch as it's usage is spread across most of the board files. Inorder to convert GPIO as platform device, modifications are required in clockxxxx_data.c file for OMAP1 so that device names can be used to obtain clock instead of getting clocks by name/NULL ptr. Use runtime pm APIs (pm_runtime_put*/pm_runtime_get*) for enabling or disabling the clocks, modify sysconfig settings and remove usage of clock FW APIs. Note 1: Converting GPIO driver to use runtime PM APIs is not done as a separate patch because GPIO clock names are different for various OMAPs and are different for some of the banks in the same CPU. This would need usage of cpu_is checks and bank id checks while using clock FW APIs in the gpio driver. Hence while making GPIO a platform driver framework, PM runtime APIs are used directly. Note 2: While implementing GPIO as a platform device, pm runtime APIs are used as mentioned above and modification is not done in gpio's prepare for idle/ resume after idle functions. This would be done in the next patch series and GPIO driver would be made to use dev_pm_ops instead of sysdev_class in that series only. Due to the above, the GPIO driver implicitly relies on CM_AUTOIDLE = 1 on its iclk for power management to work, since the driver never disables its iclk. This would be taken care in the next patch series (see Note 3 below). Refer to http://www.mail-archive.com/linux-omap@vger.kernel.org/msg39112.html for more details. Note 3: only pm_runtime_get_sync is called in gpio's probe() and pm_runtime_put* is never called. This is to make the implementation similar to the existing GPIO code. Another patch series would be sent to correct this. In OMAP3 and OMAP4 gpio's debounce clocks are optional clocks. They are enabled/ disabled whenever required using clock framework APIs TODO: 1. Cleanup the GPIO driver. Use function pointers and register offest pointers instead of using hardcoded values 2. Remove all cpu_is_ checks and OMAP specific macros 3. Remove usage of gpio_bank array so that only instance specific information is used in driver code 4. Rename 'method'/ avoid it's usage 5. Fix the non-wakeup gpios handling for OMAP2430, OMAP3 & OMAP4 6. Modify gpio's prepare for idle/ resume after idle functions to use runtime pm implentation. Signed-off-by: Charulatha V <charu@ti.com> Signed-off-by: Rajendra Nayak <rnayak@ti.com> Reviewed-by: Basak, Partha <p-basak2@ti.com> Acked-by: Kevin Hilman <khilman@deeprootsystems.com> [tony@atomide.com: updated for bank specific revision and updated boards] Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-12-08 07:26:57 +07:00
platform_set_drvdata(&omap_mpuio_device, bank);
if (platform_driver_register(&omap_mpuio_driver) == 0)
(void) platform_device_register(&omap_mpuio_device);
}
/*---------------------------------------------------------------------*/
static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
{
struct gpio_bank *bank = gpiochip_get_data(chip);
unsigned long flags;
pm_runtime_get_sync(chip->parent);
raw_spin_lock_irqsave(&bank->lock, flags);
omap_enable_gpio_module(bank, offset);
bank->mod_usage |= BIT(offset);
raw_spin_unlock_irqrestore(&bank->lock, flags);
return 0;
}
static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
{
struct gpio_bank *bank = gpiochip_get_data(chip);
unsigned long flags;
raw_spin_lock_irqsave(&bank->lock, flags);
bank->mod_usage &= ~(BIT(offset));
if (!LINE_USED(bank->irq_usage, offset)) {
omap_set_gpio_direction(bank, offset, 1);
omap_clear_gpio_debounce(bank, offset);
}
omap_disable_gpio_module(bank, offset);
raw_spin_unlock_irqrestore(&bank->lock, flags);
pm_runtime_put(chip->parent);
}
static int omap_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
{
struct gpio_bank *bank = gpiochip_get_data(chip);
return !!(readl_relaxed(bank->base + bank->regs->direction) &
BIT(offset));
}
static int omap_gpio_input(struct gpio_chip *chip, unsigned offset)
{
struct gpio_bank *bank;
unsigned long flags;
bank = gpiochip_get_data(chip);
raw_spin_lock_irqsave(&bank->lock, flags);
omap_set_gpio_direction(bank, offset, 1);
raw_spin_unlock_irqrestore(&bank->lock, flags);
return 0;
}
static int omap_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct gpio_bank *bank = gpiochip_get_data(chip);
void __iomem *reg;
if (omap_gpio_is_input(bank, offset))
reg = bank->base + bank->regs->datain;
else
reg = bank->base + bank->regs->dataout;
return (readl_relaxed(reg) & BIT(offset)) != 0;
}
static int omap_gpio_output(struct gpio_chip *chip, unsigned offset, int value)
{
struct gpio_bank *bank;
unsigned long flags;
bank = gpiochip_get_data(chip);
raw_spin_lock_irqsave(&bank->lock, flags);
bank->set_dataout(bank, offset, value);
omap_set_gpio_direction(bank, offset, 0);
raw_spin_unlock_irqrestore(&bank->lock, flags);
return 0;
}
static int omap_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
unsigned long *bits)
{
struct gpio_bank *bank = gpiochip_get_data(chip);
void __iomem *base = bank->base;
u32 direction, m, val = 0;
direction = readl_relaxed(base + bank->regs->direction);
m = direction & *mask;
if (m)
val |= readl_relaxed(base + bank->regs->datain) & m;
m = ~direction & *mask;
if (m)
val |= readl_relaxed(base + bank->regs->dataout) & m;
*bits = val;
return 0;
}
static int omap_gpio_debounce(struct gpio_chip *chip, unsigned offset,
unsigned debounce)
{
struct gpio_bank *bank;
unsigned long flags;
int ret;
bank = gpiochip_get_data(chip);
OMAP: GPIO: Implement GPIO as a platform device Implement GPIO as a platform device. GPIO APIs are used in machine_init functions. Hence it is required to complete GPIO probe before board_init. Therefore GPIO device register and driver register are implemented as postcore_initcalls. omap_gpio_init() does nothing now and this function would be removed in the next patch as it's usage is spread across most of the board files. Inorder to convert GPIO as platform device, modifications are required in clockxxxx_data.c file for OMAP1 so that device names can be used to obtain clock instead of getting clocks by name/NULL ptr. Use runtime pm APIs (pm_runtime_put*/pm_runtime_get*) for enabling or disabling the clocks, modify sysconfig settings and remove usage of clock FW APIs. Note 1: Converting GPIO driver to use runtime PM APIs is not done as a separate patch because GPIO clock names are different for various OMAPs and are different for some of the banks in the same CPU. This would need usage of cpu_is checks and bank id checks while using clock FW APIs in the gpio driver. Hence while making GPIO a platform driver framework, PM runtime APIs are used directly. Note 2: While implementing GPIO as a platform device, pm runtime APIs are used as mentioned above and modification is not done in gpio's prepare for idle/ resume after idle functions. This would be done in the next patch series and GPIO driver would be made to use dev_pm_ops instead of sysdev_class in that series only. Due to the above, the GPIO driver implicitly relies on CM_AUTOIDLE = 1 on its iclk for power management to work, since the driver never disables its iclk. This would be taken care in the next patch series (see Note 3 below). Refer to http://www.mail-archive.com/linux-omap@vger.kernel.org/msg39112.html for more details. Note 3: only pm_runtime_get_sync is called in gpio's probe() and pm_runtime_put* is never called. This is to make the implementation similar to the existing GPIO code. Another patch series would be sent to correct this. In OMAP3 and OMAP4 gpio's debounce clocks are optional clocks. They are enabled/ disabled whenever required using clock framework APIs TODO: 1. Cleanup the GPIO driver. Use function pointers and register offest pointers instead of using hardcoded values 2. Remove all cpu_is_ checks and OMAP specific macros 3. Remove usage of gpio_bank array so that only instance specific information is used in driver code 4. Rename 'method'/ avoid it's usage 5. Fix the non-wakeup gpios handling for OMAP2430, OMAP3 & OMAP4 6. Modify gpio's prepare for idle/ resume after idle functions to use runtime pm implentation. Signed-off-by: Charulatha V <charu@ti.com> Signed-off-by: Rajendra Nayak <rnayak@ti.com> Reviewed-by: Basak, Partha <p-basak2@ti.com> Acked-by: Kevin Hilman <khilman@deeprootsystems.com> [tony@atomide.com: updated for bank specific revision and updated boards] Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-12-08 07:26:57 +07:00
raw_spin_lock_irqsave(&bank->lock, flags);
ret = omap2_set_gpio_debounce(bank, offset, debounce);
raw_spin_unlock_irqrestore(&bank->lock, flags);
if (ret)
dev_info(chip->parent,
"Could not set line %u debounce to %u microseconds (%d)",
offset, debounce, ret);
return ret;
}
static int omap_gpio_set_config(struct gpio_chip *chip, unsigned offset,
unsigned long config)
{
u32 debounce;
if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE)
return -ENOTSUPP;
debounce = pinconf_to_config_argument(config);
return omap_gpio_debounce(chip, offset, debounce);
}
static void omap_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct gpio_bank *bank;
unsigned long flags;
bank = gpiochip_get_data(chip);
raw_spin_lock_irqsave(&bank->lock, flags);
bank->set_dataout(bank, offset, value);
raw_spin_unlock_irqrestore(&bank->lock, flags);
}
static void omap_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask,
unsigned long *bits)
{
struct gpio_bank *bank = gpiochip_get_data(chip);
void __iomem *reg = bank->base + bank->regs->dataout;
unsigned long flags;
u32 l;
raw_spin_lock_irqsave(&bank->lock, flags);
l = (readl_relaxed(reg) & ~*mask) | (*bits & *mask);
writel_relaxed(l, reg);
bank->context.dataout = l;
raw_spin_unlock_irqrestore(&bank->lock, flags);
}
/*---------------------------------------------------------------------*/
static void omap_gpio_show_rev(struct gpio_bank *bank)
{
static bool called;
u32 rev;
if (called || bank->regs->revision == USHRT_MAX)
return;
rev = readw_relaxed(bank->base + bank->regs->revision);
pr_info("OMAP GPIO hardware version %d.%d\n",
(rev >> 4) & 0x0f, rev & 0x0f);
called = true;
}
static void omap_gpio_mod_init(struct gpio_bank *bank)
{
void __iomem *base = bank->base;
u32 l = 0xffffffff;
if (bank->width == 16)
l = 0xffff;
if (bank->is_mpuio) {
writel_relaxed(l, bank->base + bank->regs->irqenable);
return;
}
omap_gpio_rmw(base + bank->regs->irqenable, l,
bank->regs->irqenable_inv);
omap_gpio_rmw(base + bank->regs->irqstatus, l,
!bank->regs->irqenable_inv);
if (bank->regs->debounce_en)
writel_relaxed(0, base + bank->regs->debounce_en);
/* Save OE default value (0xffffffff) in the context */
bank->context.oe = readl_relaxed(bank->base + bank->regs->direction);
/* Initialize interface clk ungated, module enabled */
if (bank->regs->ctrl)
writel_relaxed(0, base + bank->regs->ctrl);
}
static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
{
struct gpio_irq_chip *irq;
static int gpio;
const char *label;
int irq_base = 0;
int ret;
/*
* REVISIT eventually switch from OMAP-specific gpio structs
* over to the generic ones
*/
bank->chip.request = omap_gpio_request;
bank->chip.free = omap_gpio_free;
bank->chip.get_direction = omap_gpio_get_direction;
bank->chip.direction_input = omap_gpio_input;
bank->chip.get = omap_gpio_get;
bank->chip.get_multiple = omap_gpio_get_multiple;
bank->chip.direction_output = omap_gpio_output;
bank->chip.set_config = omap_gpio_set_config;
bank->chip.set = omap_gpio_set;
bank->chip.set_multiple = omap_gpio_set_multiple;
if (bank->is_mpuio) {
bank->chip.label = "mpuio";
if (bank->regs->wkup_en)
gpio: change member .dev to .parent The name .dev in a struct is normally reserved for a struct device that is let us say a superclass to the thing described by the struct. struct gpio_chip stands out by confusingly using a struct device *dev to point to the parent device (such as a platform_device) that represents the hardware. As we want to give gpio_chip:s real devices, this is not working. We need to rename this member to parent. This was done by two coccinelle scripts, I guess it is possible to combine them into one, but I don't know such stuff. They look like this: @@ struct gpio_chip *var; @@ -var->dev +var->parent and: @@ struct gpio_chip var; @@ -var.dev +var.parent and: @@ struct bgpio_chip *var; @@ -var->gc.dev +var->gc.parent Plus a few instances of bgpio that I couldn't figure out how to teach Coccinelle to rewrite. This patch hits all over the place, but I *strongly* prefer this solution to any piecemal approaches that just exercise patch mechanics all over the place. It mainly hits drivers/gpio and drivers/pinctrl which is my own backyard anyway. Cc: Haavard Skinnemoen <hskinnemoen@gmail.com> Cc: Rafał Miłecki <zajec5@gmail.com> Cc: Richard Purdie <rpurdie@rpsys.net> Cc: Mauro Carvalho Chehab <mchehab@osg.samsung.com> Cc: Alek Du <alek.du@intel.com> Cc: Jaroslav Kysela <perex@perex.cz> Cc: Takashi Iwai <tiwai@suse.com> Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Acked-by: Lee Jones <lee.jones@linaro.org> Acked-by: Jiri Kosina <jkosina@suse.cz> Acked-by: Hans-Christian Egtvedt <egtvedt@samfundet.no> Acked-by: Jacek Anaszewski <j.anaszewski@samsung.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2015-11-04 15:56:26 +07:00
bank->chip.parent = &omap_mpuio_device.dev;
bank->chip.base = OMAP_MPUIO(0);
} else {
label = devm_kasprintf(bank->chip.parent, GFP_KERNEL, "gpio-%d-%d",
gpio, gpio + bank->width - 1);
if (!label)
return -ENOMEM;
bank->chip.label = label;
bank->chip.base = gpio;
}
bank->chip.ngpio = bank->width;
#ifdef CONFIG_ARCH_OMAP1
/*
* REVISIT: Once we have OMAP1 supporting SPARSE_IRQ, we can drop
* irq_alloc_descs() since a base IRQ offset will no longer be needed.
*/
irq_base = devm_irq_alloc_descs(bank->chip.parent,
-1, 0, bank->width, 0);
if (irq_base < 0) {
dev_err(bank->chip.parent, "Couldn't allocate IRQ numbers\n");
return -ENODEV;
}
#endif
/* MPUIO is a bit different, reading IRQ status clears it */
if (bank->is_mpuio && !bank->regs->wkup_en)
irqc->irq_set_wake = NULL;
irq = &bank->chip.irq;
irq->chip = irqc;
irq->handler = handle_bad_irq;
irq->default_type = IRQ_TYPE_NONE;
irq->num_parents = 1;
irq->parents = &bank->irq;
irq->first = irq_base;
ret = gpiochip_add_data(&bank->chip, bank);
if (ret) {
dev_err(bank->chip.parent,
"Could not register gpio chip %d\n", ret);
return ret;
}
ret = devm_request_irq(bank->chip.parent, bank->irq,
omap_gpio_irq_handler,
0, dev_name(bank->chip.parent), bank);
gpio: omap: convert to use generic irq handler This patch converts TI OMAP GPIO driver to use generic irq handler instead of chained IRQ handler. This way OMAP GPIO driver will be compatible with RT kernel where it will be forced thread IRQ handler while in non-RT kernel it still will be executed in HW IRQ context. As part of this change the IRQ wakeup configuration is applied to GPIO Bank IRQ as it now will be under control of IRQ PM Core during suspend. There are also additional benefits: - on-RT kernel there will be no complains any more about PM runtime usage in atomic context "BUG: sleeping function called from invalid context"; - GPIO bank IRQs will appear in /proc/interrupts and its usage statistic will be visible; - GPIO bank IRQs could be configured through IRQ proc_fs interface and, as result, could be a part of IRQ balancing process if needed; - GPIO bank IRQs will be under control of IRQ PM Core during suspend to RAM. Disadvantage: - additional runtime overhed as call chain till omap_gpio_irq_handler() will be longer now - necessity to use wa_lock in omap_gpio_irq_handler() to W/A warning in handle_irq_event_percpu() WARNING: CPU: 1 PID: 35 at kernel/irq/handle.c:149 handle_irq_event_percpu+0x51c/0x638() This patch doesn't fully follows recommendations provided by Sebastian Andrzej Siewior [1], because It's required to go through and check all GPIO IRQ pin states as fast as possible and pass control to handle_level_irq or handle_edge_irq. handle_level_irq or handle_edge_irq will perform actions specific for IRQ triggering type and wakeup corresponding registered threaded IRQ handler (at least it's expected to be threaded). IRQs can be lost if handle_nested_irq() will be used, because excecution time of some pin specific GPIO IRQ handler can be very significant and require accessing ext. devices (I2C). Idea of such kind reworking was also discussed in [2]. [1] http://www.spinics.net/lists/linux-omap/msg120665.html [2] http://www.spinics.net/lists/linux-omap/msg119516.html Tested-by: Tony Lindgren <tony@atomide.com> Tested-by: Austin Schuh <austin@peloton-tech.com> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com> Acked-by: Santosh Shilimkar <ssantosh@kernel.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2015-09-26 02:28:03 +07:00
if (ret)
gpiochip_remove(&bank->chip);
if (!bank->is_mpuio)
gpio += bank->width;
gpio: omap: convert to use generic irq handler This patch converts TI OMAP GPIO driver to use generic irq handler instead of chained IRQ handler. This way OMAP GPIO driver will be compatible with RT kernel where it will be forced thread IRQ handler while in non-RT kernel it still will be executed in HW IRQ context. As part of this change the IRQ wakeup configuration is applied to GPIO Bank IRQ as it now will be under control of IRQ PM Core during suspend. There are also additional benefits: - on-RT kernel there will be no complains any more about PM runtime usage in atomic context "BUG: sleeping function called from invalid context"; - GPIO bank IRQs will appear in /proc/interrupts and its usage statistic will be visible; - GPIO bank IRQs could be configured through IRQ proc_fs interface and, as result, could be a part of IRQ balancing process if needed; - GPIO bank IRQs will be under control of IRQ PM Core during suspend to RAM. Disadvantage: - additional runtime overhed as call chain till omap_gpio_irq_handler() will be longer now - necessity to use wa_lock in omap_gpio_irq_handler() to W/A warning in handle_irq_event_percpu() WARNING: CPU: 1 PID: 35 at kernel/irq/handle.c:149 handle_irq_event_percpu+0x51c/0x638() This patch doesn't fully follows recommendations provided by Sebastian Andrzej Siewior [1], because It's required to go through and check all GPIO IRQ pin states as fast as possible and pass control to handle_level_irq or handle_edge_irq. handle_level_irq or handle_edge_irq will perform actions specific for IRQ triggering type and wakeup corresponding registered threaded IRQ handler (at least it's expected to be threaded). IRQs can be lost if handle_nested_irq() will be used, because excecution time of some pin specific GPIO IRQ handler can be very significant and require accessing ext. devices (I2C). Idea of such kind reworking was also discussed in [2]. [1] http://www.spinics.net/lists/linux-omap/msg120665.html [2] http://www.spinics.net/lists/linux-omap/msg119516.html Tested-by: Tony Lindgren <tony@atomide.com> Tested-by: Austin Schuh <austin@peloton-tech.com> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com> Acked-by: Santosh Shilimkar <ssantosh@kernel.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2015-09-26 02:28:03 +07:00
return ret;
}
static void omap_gpio_init_context(struct gpio_bank *p)
{
const struct omap_gpio_reg_offs *regs = p->regs;
void __iomem *base = p->base;
p->context.ctrl = readl_relaxed(base + regs->ctrl);
p->context.oe = readl_relaxed(base + regs->direction);
p->context.wake_en = readl_relaxed(base + regs->wkup_en);
p->context.leveldetect0 = readl_relaxed(base + regs->leveldetect0);
p->context.leveldetect1 = readl_relaxed(base + regs->leveldetect1);
p->context.risingdetect = readl_relaxed(base + regs->risingdetect);
p->context.fallingdetect = readl_relaxed(base + regs->fallingdetect);
p->context.irqenable1 = readl_relaxed(base + regs->irqenable);
p->context.irqenable2 = readl_relaxed(base + regs->irqenable2);
p->context.dataout = readl_relaxed(base + regs->dataout);
p->context_valid = true;
}
static void omap_gpio_restore_context(struct gpio_bank *bank)
{
const struct omap_gpio_reg_offs *regs = bank->regs;
void __iomem *base = bank->base;
writel_relaxed(bank->context.wake_en, base + regs->wkup_en);
writel_relaxed(bank->context.ctrl, base + regs->ctrl);
writel_relaxed(bank->context.leveldetect0, base + regs->leveldetect0);
writel_relaxed(bank->context.leveldetect1, base + regs->leveldetect1);
writel_relaxed(bank->context.risingdetect, base + regs->risingdetect);
writel_relaxed(bank->context.fallingdetect, base + regs->fallingdetect);
writel_relaxed(bank->context.dataout, base + regs->dataout);
writel_relaxed(bank->context.oe, base + regs->direction);
if (bank->dbck_enable_mask) {
writel_relaxed(bank->context.debounce, base + regs->debounce);
writel_relaxed(bank->context.debounce_en,
base + regs->debounce_en);
}
writel_relaxed(bank->context.irqenable1, base + regs->irqenable);
writel_relaxed(bank->context.irqenable2, base + regs->irqenable2);
}
static void omap_gpio_idle(struct gpio_bank *bank, bool may_lose_context)
{
struct device *dev = bank->chip.parent;
void __iomem *base = bank->base;
gpio: omap: Fix lost edge wake-up interrupts If an edge interrupt triggers while entering idle just before we save GPIO datain register to saved_datain, the triggered GPIO will not be noticed on wake-up. This is because the saved_datain and GPIO datain are the same on wake-up in omap_gpio_unidle(). Let's fix this by ignoring any pending edge interrupts for saved_datain. This issue affects only idle states where the GPIO module internal wake-up path is operational. For deeper idle states where the GPIO module gets powered off, Linux generic wakeirqs must be used for the padconf wake-up events with pinctrl-single driver. For examples, please see "interrupts-extended" dts usage in many drivers. This issue can be somewhat easily reproduced by pinging an idle system with smsc911x Ethernet interface configured IRQ_TYPE_EDGE_FALLING. At some point the smsc911x interrupts will just stop triggering. Also if WLCORE WLAN is used with EDGE interrupt like it's documentation specifies, we can see lost interrupts without this patch. Note that in the long run we may be able to cancel entering idle by returning an error in gpio_omap_cpu_notifier() on pending interrupts. But let's fix the bug first. Also note that because of the recent clean-up efforts this patch does not apply directly to older kernels. This does fix a long term issue though, and can be backported as needed. Cc: Aaro Koskinen <aaro.koskinen@iki.fi> Cc: Grygorii Strashko <grygorii.strashko@ti.com> Cc: Keerthy <j-keerthy@ti.com> Cc: Ladislav Michl <ladis@linux-mips.org> Cc: Peter Ujfalusi <peter.ujfalusi@ti.com> Cc: Russell King <rmk+kernel@armlinux.org.uk> Cc: Tero Kristo <t-kristo@ti.com> Signed-off-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2019-06-12 13:33:52 +07:00
u32 mask, nowake;
bank->saved_datain = readl_relaxed(base + bank->regs->datain);
gpio/omap: fix wakeups on level-triggered GPIOs While both level- and edge-triggered GPIOs are capable of generating interrupts, only edge-triggered GPIOs are capable of generating a module-level wakeup to the PRCM (c.f. 34xx NDA TRM section 25.5.3.2.) In order to ensure that devices using level-triggered GPIOs as interrupts can also cause wakeups (e.g. from idle), this patch enables edge-triggering for wakeup-enabled, level-triggered GPIOs when a GPIO bank is runtime-suspended (which also happens during idle.) This fixes a problem found in GPMC-connected network cards with GPIO interrupts (e.g. smsc911x on Zoom3, Overo, ...) where network booting with NFSroot was very slow since the GPIO IRQs used by the NIC were not generating PRCM wakeups, and thus not waking the system from idle. NOTE: until v3.3, this boot-time problem was somewhat masked because the UART init prevented WFI during boot until the full serial driver was available. Preventing WFI allowed regular GPIO interrupts to fire and this problem was not seen. After the UART runtime PM cleanups, we no longer avoid WFI during boot, so GPIO IRQs that were not causing wakeups resulted in very slow IRQ response times. Tested on platforms using level-triggered GPIOs for network IRQs using the SMSC911x NIC: 3530/Overo and 3630/Zoom3. Reported-by: Tony Lindgren <tony@atomide.com> Tested-by: Tarun Kanti DebBarma <tarun.kanti@ti.com> Tested-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Kevin Hilman <khilman@ti.com> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
2012-03-06 06:10:04 +07:00
if (!bank->enabled_non_wakeup_gpios)
goto update_gpio_context_count;
gpio: omap: Fix lost edge wake-up interrupts If an edge interrupt triggers while entering idle just before we save GPIO datain register to saved_datain, the triggered GPIO will not be noticed on wake-up. This is because the saved_datain and GPIO datain are the same on wake-up in omap_gpio_unidle(). Let's fix this by ignoring any pending edge interrupts for saved_datain. This issue affects only idle states where the GPIO module internal wake-up path is operational. For deeper idle states where the GPIO module gets powered off, Linux generic wakeirqs must be used for the padconf wake-up events with pinctrl-single driver. For examples, please see "interrupts-extended" dts usage in many drivers. This issue can be somewhat easily reproduced by pinging an idle system with smsc911x Ethernet interface configured IRQ_TYPE_EDGE_FALLING. At some point the smsc911x interrupts will just stop triggering. Also if WLCORE WLAN is used with EDGE interrupt like it's documentation specifies, we can see lost interrupts without this patch. Note that in the long run we may be able to cancel entering idle by returning an error in gpio_omap_cpu_notifier() on pending interrupts. But let's fix the bug first. Also note that because of the recent clean-up efforts this patch does not apply directly to older kernels. This does fix a long term issue though, and can be backported as needed. Cc: Aaro Koskinen <aaro.koskinen@iki.fi> Cc: Grygorii Strashko <grygorii.strashko@ti.com> Cc: Keerthy <j-keerthy@ti.com> Cc: Ladislav Michl <ladis@linux-mips.org> Cc: Peter Ujfalusi <peter.ujfalusi@ti.com> Cc: Russell King <rmk+kernel@armlinux.org.uk> Cc: Tero Kristo <t-kristo@ti.com> Signed-off-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2019-06-12 13:33:52 +07:00
/* Check for pending EDGE_FALLING, ignore EDGE_BOTH */
mask = bank->enabled_non_wakeup_gpios & bank->context.fallingdetect;
mask &= ~bank->context.risingdetect;
bank->saved_datain |= mask;
/* Check for pending EDGE_RISING, ignore EDGE_BOTH */
mask = bank->enabled_non_wakeup_gpios & bank->context.risingdetect;
mask &= ~bank->context.fallingdetect;
bank->saved_datain &= ~mask;
if (!may_lose_context)
goto update_gpio_context_count;
/*
* If going to OFF, remove triggering for all wkup domain
* non-wakeup GPIOs. Otherwise spurious IRQs will be
* generated. See OMAP2420 Errata item 1.101.
*/
if (!bank->loses_context && bank->enabled_non_wakeup_gpios) {
nowake = bank->enabled_non_wakeup_gpios;
omap_gpio_rmw(base + bank->regs->fallingdetect, nowake, ~nowake);
omap_gpio_rmw(base + bank->regs->risingdetect, nowake, ~nowake);
}
update_gpio_context_count:
if (bank->get_context_loss_count)
bank->context_loss_count =
bank->get_context_loss_count(dev);
omap_gpio_dbck_disable(bank);
}
static void omap_gpio_unidle(struct gpio_bank *bank)
{
struct device *dev = bank->chip.parent;
u32 l = 0, gen, gen0, gen1;
int c;
/*
* On the first resume during the probe, the context has not
* been initialised and so initialise it now. Also initialise
* the context loss count.
*/
if (bank->loses_context && !bank->context_valid) {
omap_gpio_init_context(bank);
if (bank->get_context_loss_count)
bank->context_loss_count =
bank->get_context_loss_count(dev);
}
omap_gpio_dbck_enable(bank);
gpio/omap: fix wakeups on level-triggered GPIOs While both level- and edge-triggered GPIOs are capable of generating interrupts, only edge-triggered GPIOs are capable of generating a module-level wakeup to the PRCM (c.f. 34xx NDA TRM section 25.5.3.2.) In order to ensure that devices using level-triggered GPIOs as interrupts can also cause wakeups (e.g. from idle), this patch enables edge-triggering for wakeup-enabled, level-triggered GPIOs when a GPIO bank is runtime-suspended (which also happens during idle.) This fixes a problem found in GPMC-connected network cards with GPIO interrupts (e.g. smsc911x on Zoom3, Overo, ...) where network booting with NFSroot was very slow since the GPIO IRQs used by the NIC were not generating PRCM wakeups, and thus not waking the system from idle. NOTE: until v3.3, this boot-time problem was somewhat masked because the UART init prevented WFI during boot until the full serial driver was available. Preventing WFI allowed regular GPIO interrupts to fire and this problem was not seen. After the UART runtime PM cleanups, we no longer avoid WFI during boot, so GPIO IRQs that were not causing wakeups resulted in very slow IRQ response times. Tested on platforms using level-triggered GPIOs for network IRQs using the SMSC911x NIC: 3530/Overo and 3630/Zoom3. Reported-by: Tony Lindgren <tony@atomide.com> Tested-by: Tarun Kanti DebBarma <tarun.kanti@ti.com> Tested-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Kevin Hilman <khilman@ti.com> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
2012-03-06 06:10:04 +07:00
if (bank->loses_context) {
if (!bank->get_context_loss_count) {
omap_gpio_restore_context(bank);
} else {
c = bank->get_context_loss_count(dev);
if (c != bank->context_loss_count) {
omap_gpio_restore_context(bank);
} else {
return;
}
}
} else {
/* Restore changes done for OMAP2420 errata 1.101 */
writel_relaxed(bank->context.fallingdetect,
bank->base + bank->regs->fallingdetect);
writel_relaxed(bank->context.risingdetect,
bank->base + bank->regs->risingdetect);
}
l = readl_relaxed(bank->base + bank->regs->datain);
/*
* Check if any of the non-wakeup interrupt GPIOs have changed
* state. If so, generate an IRQ by software. This is
* horribly racy, but it's the best we can do to work around
* this silicon bug.
*/
l ^= bank->saved_datain;
l &= bank->enabled_non_wakeup_gpios;
/*
* No need to generate IRQs for the rising edge for gpio IRQs
* configured with falling edge only; and vice versa.
*/
gen0 = l & bank->context.fallingdetect;
gen0 &= bank->saved_datain;
gen1 = l & bank->context.risingdetect;
gen1 &= ~(bank->saved_datain);
/* FIXME: Consider GPIO IRQs with level detections properly! */
gen = l & (~(bank->context.fallingdetect) &
~(bank->context.risingdetect));
/* Consider all GPIO IRQs needed to be updated */
gen |= gen0 | gen1;
if (gen) {
u32 old0, old1;
old0 = readl_relaxed(bank->base + bank->regs->leveldetect0);
old1 = readl_relaxed(bank->base + bank->regs->leveldetect1);
if (!bank->regs->irqstatus_raw0) {
writel_relaxed(old0 | gen, bank->base +
bank->regs->leveldetect0);
writel_relaxed(old1 | gen, bank->base +
bank->regs->leveldetect1);
}
if (bank->regs->irqstatus_raw0) {
writel_relaxed(old0 | l, bank->base +
bank->regs->leveldetect0);
writel_relaxed(old1 | l, bank->base +
bank->regs->leveldetect1);
}
writel_relaxed(old0, bank->base + bank->regs->leveldetect0);
writel_relaxed(old1, bank->base + bank->regs->leveldetect1);
}
}
static int gpio_omap_cpu_notifier(struct notifier_block *nb,
unsigned long cmd, void *v)
{
struct gpio_bank *bank;
unsigned long flags;
bank = container_of(nb, struct gpio_bank, nb);
raw_spin_lock_irqsave(&bank->lock, flags);
switch (cmd) {
case CPU_CLUSTER_PM_ENTER:
if (bank->is_suspended)
break;
omap_gpio_idle(bank, true);
break;
case CPU_CLUSTER_PM_ENTER_FAILED:
case CPU_CLUSTER_PM_EXIT:
if (bank->is_suspended)
break;
omap_gpio_unidle(bank);
break;
}
raw_spin_unlock_irqrestore(&bank->lock, flags);
return NOTIFY_OK;
}
static const 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 const 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,
.irqstatus_raw0 = OMAP4_GPIO_IRQSTATUSRAW0,
.irqstatus_raw1 = OMAP4_GPIO_IRQSTATUSRAW1,
.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 const struct omap_gpio_platform_data omap2_pdata = {
.regs = &omap2_gpio_regs,
.bank_width = 32,
.dbck_flag = false,
};
static const struct omap_gpio_platform_data omap3_pdata = {
.regs = &omap2_gpio_regs,
.bank_width = 32,
.dbck_flag = true,
};
static const 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);
static int 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;
const struct omap_gpio_platform_data *pdata;
struct gpio_bank *bank;
struct irq_chip *irqc;
int ret;
match = of_match_device(of_match_ptr(omap_gpio_match), dev);
pdata = match ? match->data : dev_get_platdata(dev);
if (!pdata)
return -EINVAL;
bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL);
if (!bank)
return -ENOMEM;
irqc = devm_kzalloc(dev, sizeof(*irqc), GFP_KERNEL);
if (!irqc)
return -ENOMEM;
irqc->irq_startup = omap_gpio_irq_startup,
irqc->irq_shutdown = omap_gpio_irq_shutdown,
irqc->irq_ack = dummy_irq_chip.irq_ack,
irqc->irq_mask = omap_gpio_mask_irq,
irqc->irq_unmask = omap_gpio_unmask_irq,
irqc->irq_set_type = omap_gpio_irq_type,
irqc->irq_set_wake = omap_gpio_wake_enable,
irqc->irq_bus_lock = omap_gpio_irq_bus_lock,
irqc->irq_bus_sync_unlock = gpio_irq_bus_sync_unlock,
irqc->name = dev_name(&pdev->dev);
irqc->flags = IRQCHIP_MASK_ON_SUSPEND;
irqc->parent_device = dev;
bank->irq = platform_get_irq(pdev, 0);
if (bank->irq <= 0) {
if (!bank->irq)
bank->irq = -ENXIO;
if (bank->irq != -EPROBE_DEFER)
dev_err(dev,
"can't get irq resource ret=%d\n", bank->irq);
return bank->irq;
}
bank->chip.parent = dev;
bank->chip.owner = THIS_MODULE;
bank->dbck_flag = pdata->dbck_flag;
bank->stride = pdata->bank_stride;
bank->width = pdata->bank_width;
bank->is_mpuio = pdata->is_mpuio;
bank->non_wakeup_gpios = pdata->non_wakeup_gpios;
bank->regs = pdata->regs;
#ifdef CONFIG_OF_GPIO
bank->chip.of_node = of_node_get(node);
#endif
if (node) {
if (!of_property_read_bool(node, "ti,gpio-always-on"))
bank->loses_context = true;
} else {
bank->loses_context = pdata->loses_context;
if (bank->loses_context)
bank->get_context_loss_count =
pdata->get_context_loss_count;
}
if (bank->regs->set_dataout && bank->regs->clr_dataout)
bank->set_dataout = omap_set_gpio_dataout_reg;
else
bank->set_dataout = omap_set_gpio_dataout_mask;
raw_spin_lock_init(&bank->lock);
raw_spin_lock_init(&bank->wa_lock);
/* Static mapping, never released */
bank->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(bank->base)) {
return PTR_ERR(bank->base);
}
if (bank->dbck_flag) {
bank->dbck = devm_clk_get(dev, "dbclk");
if (IS_ERR(bank->dbck)) {
dev_err(dev,
"Could not get gpio dbck. Disable debounce\n");
bank->dbck_flag = false;
} else {
clk_prepare(bank->dbck);
}
}
platform_set_drvdata(pdev, bank);
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
if (bank->is_mpuio)
omap_mpuio_init(bank);
omap_gpio_mod_init(bank);
ret = omap_gpio_chip_init(bank, irqc);
if (ret) {
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
if (bank->dbck_flag)
clk_unprepare(bank->dbck);
return ret;
}
omap_gpio_show_rev(bank);
bank->nb.notifier_call = gpio_omap_cpu_notifier;
cpu_pm_register_notifier(&bank->nb);
pm_runtime_put(dev);
return 0;
}
static int omap_gpio_remove(struct platform_device *pdev)
{
struct gpio_bank *bank = platform_get_drvdata(pdev);
cpu_pm_unregister_notifier(&bank->nb);
gpiochip_remove(&bank->chip);
pm_runtime_disable(&pdev->dev);
if (bank->dbck_flag)
clk_unprepare(bank->dbck);
return 0;
}
static int __maybe_unused omap_gpio_runtime_suspend(struct device *dev)
{
struct gpio_bank *bank = dev_get_drvdata(dev);
unsigned long flags;
raw_spin_lock_irqsave(&bank->lock, flags);
omap_gpio_idle(bank, true);
bank->is_suspended = true;
raw_spin_unlock_irqrestore(&bank->lock, flags);
gpio: gpio-omap: Remove conditional pm_runtime handling for GPIO interrupts Commit b764a5863fd8 ("gpio: omap: Remove custom PM calls and use cpu_pm instead") moved interrupt using GPIO banks to idle with cpu_pm in order to drop the use of pm_runtime_irq_safe() in a later patch. The GPIO banks with no interrupts claimed are still being idled based on PM runtime calls. However this caused a regression for am437x suspend for rtc+ddr idle mode where the device cannot enter idle state as reported by Keerthy <j-keerthy@ti.com>. To fix the issue, we must not fail the pm_runtime callbacks. For GPIO interrupts, we already have irq_chip_pm_get increment the PM runtime use count as pointed out by Grygorii Strashko <grygorii.strashko@ti.com>. So all we need to do is remove the conditional handling in the runtime_suspend and resume functions and let the CPU PM notifier idle the GPIO banks. Cc: Aaro Koskinen <aaro.koskinen@iki.fi> Cc: Grygorii Strashko <grygorii.strashko@ti.com> Cc: Keerthy <j-keerthy@ti.com> Cc: Peter Ujfalusi <peter.ujfalusi@ti.com> Cc: Tero Kristo <t-kristo@ti.com> Fixes: b764a5863fd8 ("gpio: omap: Remove custom PM calls and use cpu_pm instead") Reported-by: Keerthy <j-keerthy@ti.com> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> [tony@atomide.com: updated patch description, dropped runtime count changes] Signed-off-by: Tony Lindgren <tony@atomide.com> Tested-by: Keerthy <j-keerthy@ti.com> Reviewed-by: Keerthy <j-keerthy@ti.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2019-04-11 02:51:13 +07:00
return 0;
}
static int __maybe_unused omap_gpio_runtime_resume(struct device *dev)
{
struct gpio_bank *bank = dev_get_drvdata(dev);
unsigned long flags;
raw_spin_lock_irqsave(&bank->lock, flags);
omap_gpio_unidle(bank);
bank->is_suspended = false;
raw_spin_unlock_irqrestore(&bank->lock, flags);
gpio: gpio-omap: Remove conditional pm_runtime handling for GPIO interrupts Commit b764a5863fd8 ("gpio: omap: Remove custom PM calls and use cpu_pm instead") moved interrupt using GPIO banks to idle with cpu_pm in order to drop the use of pm_runtime_irq_safe() in a later patch. The GPIO banks with no interrupts claimed are still being idled based on PM runtime calls. However this caused a regression for am437x suspend for rtc+ddr idle mode where the device cannot enter idle state as reported by Keerthy <j-keerthy@ti.com>. To fix the issue, we must not fail the pm_runtime callbacks. For GPIO interrupts, we already have irq_chip_pm_get increment the PM runtime use count as pointed out by Grygorii Strashko <grygorii.strashko@ti.com>. So all we need to do is remove the conditional handling in the runtime_suspend and resume functions and let the CPU PM notifier idle the GPIO banks. Cc: Aaro Koskinen <aaro.koskinen@iki.fi> Cc: Grygorii Strashko <grygorii.strashko@ti.com> Cc: Keerthy <j-keerthy@ti.com> Cc: Peter Ujfalusi <peter.ujfalusi@ti.com> Cc: Tero Kristo <t-kristo@ti.com> Fixes: b764a5863fd8 ("gpio: omap: Remove custom PM calls and use cpu_pm instead") Reported-by: Keerthy <j-keerthy@ti.com> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> [tony@atomide.com: updated patch description, dropped runtime count changes] Signed-off-by: Tony Lindgren <tony@atomide.com> Tested-by: Keerthy <j-keerthy@ti.com> Reviewed-by: Keerthy <j-keerthy@ti.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
2019-04-11 02:51:13 +07:00
return 0;
}
static const struct dev_pm_ops gpio_pm_ops = {
SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume,
NULL)
};
OMAP: GPIO: Implement GPIO as a platform device Implement GPIO as a platform device. GPIO APIs are used in machine_init functions. Hence it is required to complete GPIO probe before board_init. Therefore GPIO device register and driver register are implemented as postcore_initcalls. omap_gpio_init() does nothing now and this function would be removed in the next patch as it's usage is spread across most of the board files. Inorder to convert GPIO as platform device, modifications are required in clockxxxx_data.c file for OMAP1 so that device names can be used to obtain clock instead of getting clocks by name/NULL ptr. Use runtime pm APIs (pm_runtime_put*/pm_runtime_get*) for enabling or disabling the clocks, modify sysconfig settings and remove usage of clock FW APIs. Note 1: Converting GPIO driver to use runtime PM APIs is not done as a separate patch because GPIO clock names are different for various OMAPs and are different for some of the banks in the same CPU. This would need usage of cpu_is checks and bank id checks while using clock FW APIs in the gpio driver. Hence while making GPIO a platform driver framework, PM runtime APIs are used directly. Note 2: While implementing GPIO as a platform device, pm runtime APIs are used as mentioned above and modification is not done in gpio's prepare for idle/ resume after idle functions. This would be done in the next patch series and GPIO driver would be made to use dev_pm_ops instead of sysdev_class in that series only. Due to the above, the GPIO driver implicitly relies on CM_AUTOIDLE = 1 on its iclk for power management to work, since the driver never disables its iclk. This would be taken care in the next patch series (see Note 3 below). Refer to http://www.mail-archive.com/linux-omap@vger.kernel.org/msg39112.html for more details. Note 3: only pm_runtime_get_sync is called in gpio's probe() and pm_runtime_put* is never called. This is to make the implementation similar to the existing GPIO code. Another patch series would be sent to correct this. In OMAP3 and OMAP4 gpio's debounce clocks are optional clocks. They are enabled/ disabled whenever required using clock framework APIs TODO: 1. Cleanup the GPIO driver. Use function pointers and register offest pointers instead of using hardcoded values 2. Remove all cpu_is_ checks and OMAP specific macros 3. Remove usage of gpio_bank array so that only instance specific information is used in driver code 4. Rename 'method'/ avoid it's usage 5. Fix the non-wakeup gpios handling for OMAP2430, OMAP3 & OMAP4 6. Modify gpio's prepare for idle/ resume after idle functions to use runtime pm implentation. Signed-off-by: Charulatha V <charu@ti.com> Signed-off-by: Rajendra Nayak <rnayak@ti.com> Reviewed-by: Basak, Partha <p-basak2@ti.com> Acked-by: Kevin Hilman <khilman@deeprootsystems.com> [tony@atomide.com: updated for bank specific revision and updated boards] Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-12-08 07:26:57 +07:00
static struct platform_driver omap_gpio_driver = {
.probe = omap_gpio_probe,
.remove = omap_gpio_remove,
OMAP: GPIO: Implement GPIO as a platform device Implement GPIO as a platform device. GPIO APIs are used in machine_init functions. Hence it is required to complete GPIO probe before board_init. Therefore GPIO device register and driver register are implemented as postcore_initcalls. omap_gpio_init() does nothing now and this function would be removed in the next patch as it's usage is spread across most of the board files. Inorder to convert GPIO as platform device, modifications are required in clockxxxx_data.c file for OMAP1 so that device names can be used to obtain clock instead of getting clocks by name/NULL ptr. Use runtime pm APIs (pm_runtime_put*/pm_runtime_get*) for enabling or disabling the clocks, modify sysconfig settings and remove usage of clock FW APIs. Note 1: Converting GPIO driver to use runtime PM APIs is not done as a separate patch because GPIO clock names are different for various OMAPs and are different for some of the banks in the same CPU. This would need usage of cpu_is checks and bank id checks while using clock FW APIs in the gpio driver. Hence while making GPIO a platform driver framework, PM runtime APIs are used directly. Note 2: While implementing GPIO as a platform device, pm runtime APIs are used as mentioned above and modification is not done in gpio's prepare for idle/ resume after idle functions. This would be done in the next patch series and GPIO driver would be made to use dev_pm_ops instead of sysdev_class in that series only. Due to the above, the GPIO driver implicitly relies on CM_AUTOIDLE = 1 on its iclk for power management to work, since the driver never disables its iclk. This would be taken care in the next patch series (see Note 3 below). Refer to http://www.mail-archive.com/linux-omap@vger.kernel.org/msg39112.html for more details. Note 3: only pm_runtime_get_sync is called in gpio's probe() and pm_runtime_put* is never called. This is to make the implementation similar to the existing GPIO code. Another patch series would be sent to correct this. In OMAP3 and OMAP4 gpio's debounce clocks are optional clocks. They are enabled/ disabled whenever required using clock framework APIs TODO: 1. Cleanup the GPIO driver. Use function pointers and register offest pointers instead of using hardcoded values 2. Remove all cpu_is_ checks and OMAP specific macros 3. Remove usage of gpio_bank array so that only instance specific information is used in driver code 4. Rename 'method'/ avoid it's usage 5. Fix the non-wakeup gpios handling for OMAP2430, OMAP3 & OMAP4 6. Modify gpio's prepare for idle/ resume after idle functions to use runtime pm implentation. Signed-off-by: Charulatha V <charu@ti.com> Signed-off-by: Rajendra Nayak <rnayak@ti.com> Reviewed-by: Basak, Partha <p-basak2@ti.com> Acked-by: Kevin Hilman <khilman@deeprootsystems.com> [tony@atomide.com: updated for bank specific revision and updated boards] Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-12-08 07:26:57 +07:00
.driver = {
.name = "omap_gpio",
.pm = &gpio_pm_ops,
.of_match_table = omap_gpio_match,
OMAP: GPIO: Implement GPIO as a platform device Implement GPIO as a platform device. GPIO APIs are used in machine_init functions. Hence it is required to complete GPIO probe before board_init. Therefore GPIO device register and driver register are implemented as postcore_initcalls. omap_gpio_init() does nothing now and this function would be removed in the next patch as it's usage is spread across most of the board files. Inorder to convert GPIO as platform device, modifications are required in clockxxxx_data.c file for OMAP1 so that device names can be used to obtain clock instead of getting clocks by name/NULL ptr. Use runtime pm APIs (pm_runtime_put*/pm_runtime_get*) for enabling or disabling the clocks, modify sysconfig settings and remove usage of clock FW APIs. Note 1: Converting GPIO driver to use runtime PM APIs is not done as a separate patch because GPIO clock names are different for various OMAPs and are different for some of the banks in the same CPU. This would need usage of cpu_is checks and bank id checks while using clock FW APIs in the gpio driver. Hence while making GPIO a platform driver framework, PM runtime APIs are used directly. Note 2: While implementing GPIO as a platform device, pm runtime APIs are used as mentioned above and modification is not done in gpio's prepare for idle/ resume after idle functions. This would be done in the next patch series and GPIO driver would be made to use dev_pm_ops instead of sysdev_class in that series only. Due to the above, the GPIO driver implicitly relies on CM_AUTOIDLE = 1 on its iclk for power management to work, since the driver never disables its iclk. This would be taken care in the next patch series (see Note 3 below). Refer to http://www.mail-archive.com/linux-omap@vger.kernel.org/msg39112.html for more details. Note 3: only pm_runtime_get_sync is called in gpio's probe() and pm_runtime_put* is never called. This is to make the implementation similar to the existing GPIO code. Another patch series would be sent to correct this. In OMAP3 and OMAP4 gpio's debounce clocks are optional clocks. They are enabled/ disabled whenever required using clock framework APIs TODO: 1. Cleanup the GPIO driver. Use function pointers and register offest pointers instead of using hardcoded values 2. Remove all cpu_is_ checks and OMAP specific macros 3. Remove usage of gpio_bank array so that only instance specific information is used in driver code 4. Rename 'method'/ avoid it's usage 5. Fix the non-wakeup gpios handling for OMAP2430, OMAP3 & OMAP4 6. Modify gpio's prepare for idle/ resume after idle functions to use runtime pm implentation. Signed-off-by: Charulatha V <charu@ti.com> Signed-off-by: Rajendra Nayak <rnayak@ti.com> Reviewed-by: Basak, Partha <p-basak2@ti.com> Acked-by: Kevin Hilman <khilman@deeprootsystems.com> [tony@atomide.com: updated for bank specific revision and updated boards] Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-12-08 07:26:57 +07:00
},
};
/*
OMAP: GPIO: Implement GPIO as a platform device Implement GPIO as a platform device. GPIO APIs are used in machine_init functions. Hence it is required to complete GPIO probe before board_init. Therefore GPIO device register and driver register are implemented as postcore_initcalls. omap_gpio_init() does nothing now and this function would be removed in the next patch as it's usage is spread across most of the board files. Inorder to convert GPIO as platform device, modifications are required in clockxxxx_data.c file for OMAP1 so that device names can be used to obtain clock instead of getting clocks by name/NULL ptr. Use runtime pm APIs (pm_runtime_put*/pm_runtime_get*) for enabling or disabling the clocks, modify sysconfig settings and remove usage of clock FW APIs. Note 1: Converting GPIO driver to use runtime PM APIs is not done as a separate patch because GPIO clock names are different for various OMAPs and are different for some of the banks in the same CPU. This would need usage of cpu_is checks and bank id checks while using clock FW APIs in the gpio driver. Hence while making GPIO a platform driver framework, PM runtime APIs are used directly. Note 2: While implementing GPIO as a platform device, pm runtime APIs are used as mentioned above and modification is not done in gpio's prepare for idle/ resume after idle functions. This would be done in the next patch series and GPIO driver would be made to use dev_pm_ops instead of sysdev_class in that series only. Due to the above, the GPIO driver implicitly relies on CM_AUTOIDLE = 1 on its iclk for power management to work, since the driver never disables its iclk. This would be taken care in the next patch series (see Note 3 below). Refer to http://www.mail-archive.com/linux-omap@vger.kernel.org/msg39112.html for more details. Note 3: only pm_runtime_get_sync is called in gpio's probe() and pm_runtime_put* is never called. This is to make the implementation similar to the existing GPIO code. Another patch series would be sent to correct this. In OMAP3 and OMAP4 gpio's debounce clocks are optional clocks. They are enabled/ disabled whenever required using clock framework APIs TODO: 1. Cleanup the GPIO driver. Use function pointers and register offest pointers instead of using hardcoded values 2. Remove all cpu_is_ checks and OMAP specific macros 3. Remove usage of gpio_bank array so that only instance specific information is used in driver code 4. Rename 'method'/ avoid it's usage 5. Fix the non-wakeup gpios handling for OMAP2430, OMAP3 & OMAP4 6. Modify gpio's prepare for idle/ resume after idle functions to use runtime pm implentation. Signed-off-by: Charulatha V <charu@ti.com> Signed-off-by: Rajendra Nayak <rnayak@ti.com> Reviewed-by: Basak, Partha <p-basak2@ti.com> Acked-by: Kevin Hilman <khilman@deeprootsystems.com> [tony@atomide.com: updated for bank specific revision and updated boards] Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-12-08 07:26:57 +07:00
* gpio driver register needs to be done before
* machine_init functions access gpio APIs.
* Hence omap_gpio_drv_reg() is a postcore_initcall.
*/
OMAP: GPIO: Implement GPIO as a platform device Implement GPIO as a platform device. GPIO APIs are used in machine_init functions. Hence it is required to complete GPIO probe before board_init. Therefore GPIO device register and driver register are implemented as postcore_initcalls. omap_gpio_init() does nothing now and this function would be removed in the next patch as it's usage is spread across most of the board files. Inorder to convert GPIO as platform device, modifications are required in clockxxxx_data.c file for OMAP1 so that device names can be used to obtain clock instead of getting clocks by name/NULL ptr. Use runtime pm APIs (pm_runtime_put*/pm_runtime_get*) for enabling or disabling the clocks, modify sysconfig settings and remove usage of clock FW APIs. Note 1: Converting GPIO driver to use runtime PM APIs is not done as a separate patch because GPIO clock names are different for various OMAPs and are different for some of the banks in the same CPU. This would need usage of cpu_is checks and bank id checks while using clock FW APIs in the gpio driver. Hence while making GPIO a platform driver framework, PM runtime APIs are used directly. Note 2: While implementing GPIO as a platform device, pm runtime APIs are used as mentioned above and modification is not done in gpio's prepare for idle/ resume after idle functions. This would be done in the next patch series and GPIO driver would be made to use dev_pm_ops instead of sysdev_class in that series only. Due to the above, the GPIO driver implicitly relies on CM_AUTOIDLE = 1 on its iclk for power management to work, since the driver never disables its iclk. This would be taken care in the next patch series (see Note 3 below). Refer to http://www.mail-archive.com/linux-omap@vger.kernel.org/msg39112.html for more details. Note 3: only pm_runtime_get_sync is called in gpio's probe() and pm_runtime_put* is never called. This is to make the implementation similar to the existing GPIO code. Another patch series would be sent to correct this. In OMAP3 and OMAP4 gpio's debounce clocks are optional clocks. They are enabled/ disabled whenever required using clock framework APIs TODO: 1. Cleanup the GPIO driver. Use function pointers and register offest pointers instead of using hardcoded values 2. Remove all cpu_is_ checks and OMAP specific macros 3. Remove usage of gpio_bank array so that only instance specific information is used in driver code 4. Rename 'method'/ avoid it's usage 5. Fix the non-wakeup gpios handling for OMAP2430, OMAP3 & OMAP4 6. Modify gpio's prepare for idle/ resume after idle functions to use runtime pm implentation. Signed-off-by: Charulatha V <charu@ti.com> Signed-off-by: Rajendra Nayak <rnayak@ti.com> Reviewed-by: Basak, Partha <p-basak2@ti.com> Acked-by: Kevin Hilman <khilman@deeprootsystems.com> [tony@atomide.com: updated for bank specific revision and updated boards] Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-12-08 07:26:57 +07:00
static int __init omap_gpio_drv_reg(void)
{
OMAP: GPIO: Implement GPIO as a platform device Implement GPIO as a platform device. GPIO APIs are used in machine_init functions. Hence it is required to complete GPIO probe before board_init. Therefore GPIO device register and driver register are implemented as postcore_initcalls. omap_gpio_init() does nothing now and this function would be removed in the next patch as it's usage is spread across most of the board files. Inorder to convert GPIO as platform device, modifications are required in clockxxxx_data.c file for OMAP1 so that device names can be used to obtain clock instead of getting clocks by name/NULL ptr. Use runtime pm APIs (pm_runtime_put*/pm_runtime_get*) for enabling or disabling the clocks, modify sysconfig settings and remove usage of clock FW APIs. Note 1: Converting GPIO driver to use runtime PM APIs is not done as a separate patch because GPIO clock names are different for various OMAPs and are different for some of the banks in the same CPU. This would need usage of cpu_is checks and bank id checks while using clock FW APIs in the gpio driver. Hence while making GPIO a platform driver framework, PM runtime APIs are used directly. Note 2: While implementing GPIO as a platform device, pm runtime APIs are used as mentioned above and modification is not done in gpio's prepare for idle/ resume after idle functions. This would be done in the next patch series and GPIO driver would be made to use dev_pm_ops instead of sysdev_class in that series only. Due to the above, the GPIO driver implicitly relies on CM_AUTOIDLE = 1 on its iclk for power management to work, since the driver never disables its iclk. This would be taken care in the next patch series (see Note 3 below). Refer to http://www.mail-archive.com/linux-omap@vger.kernel.org/msg39112.html for more details. Note 3: only pm_runtime_get_sync is called in gpio's probe() and pm_runtime_put* is never called. This is to make the implementation similar to the existing GPIO code. Another patch series would be sent to correct this. In OMAP3 and OMAP4 gpio's debounce clocks are optional clocks. They are enabled/ disabled whenever required using clock framework APIs TODO: 1. Cleanup the GPIO driver. Use function pointers and register offest pointers instead of using hardcoded values 2. Remove all cpu_is_ checks and OMAP specific macros 3. Remove usage of gpio_bank array so that only instance specific information is used in driver code 4. Rename 'method'/ avoid it's usage 5. Fix the non-wakeup gpios handling for OMAP2430, OMAP3 & OMAP4 6. Modify gpio's prepare for idle/ resume after idle functions to use runtime pm implentation. Signed-off-by: Charulatha V <charu@ti.com> Signed-off-by: Rajendra Nayak <rnayak@ti.com> Reviewed-by: Basak, Partha <p-basak2@ti.com> Acked-by: Kevin Hilman <khilman@deeprootsystems.com> [tony@atomide.com: updated for bank specific revision and updated boards] Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-12-08 07:26:57 +07:00
return platform_driver_register(&omap_gpio_driver);
}
OMAP: GPIO: Implement GPIO as a platform device Implement GPIO as a platform device. GPIO APIs are used in machine_init functions. Hence it is required to complete GPIO probe before board_init. Therefore GPIO device register and driver register are implemented as postcore_initcalls. omap_gpio_init() does nothing now and this function would be removed in the next patch as it's usage is spread across most of the board files. Inorder to convert GPIO as platform device, modifications are required in clockxxxx_data.c file for OMAP1 so that device names can be used to obtain clock instead of getting clocks by name/NULL ptr. Use runtime pm APIs (pm_runtime_put*/pm_runtime_get*) for enabling or disabling the clocks, modify sysconfig settings and remove usage of clock FW APIs. Note 1: Converting GPIO driver to use runtime PM APIs is not done as a separate patch because GPIO clock names are different for various OMAPs and are different for some of the banks in the same CPU. This would need usage of cpu_is checks and bank id checks while using clock FW APIs in the gpio driver. Hence while making GPIO a platform driver framework, PM runtime APIs are used directly. Note 2: While implementing GPIO as a platform device, pm runtime APIs are used as mentioned above and modification is not done in gpio's prepare for idle/ resume after idle functions. This would be done in the next patch series and GPIO driver would be made to use dev_pm_ops instead of sysdev_class in that series only. Due to the above, the GPIO driver implicitly relies on CM_AUTOIDLE = 1 on its iclk for power management to work, since the driver never disables its iclk. This would be taken care in the next patch series (see Note 3 below). Refer to http://www.mail-archive.com/linux-omap@vger.kernel.org/msg39112.html for more details. Note 3: only pm_runtime_get_sync is called in gpio's probe() and pm_runtime_put* is never called. This is to make the implementation similar to the existing GPIO code. Another patch series would be sent to correct this. In OMAP3 and OMAP4 gpio's debounce clocks are optional clocks. They are enabled/ disabled whenever required using clock framework APIs TODO: 1. Cleanup the GPIO driver. Use function pointers and register offest pointers instead of using hardcoded values 2. Remove all cpu_is_ checks and OMAP specific macros 3. Remove usage of gpio_bank array so that only instance specific information is used in driver code 4. Rename 'method'/ avoid it's usage 5. Fix the non-wakeup gpios handling for OMAP2430, OMAP3 & OMAP4 6. Modify gpio's prepare for idle/ resume after idle functions to use runtime pm implentation. Signed-off-by: Charulatha V <charu@ti.com> Signed-off-by: Rajendra Nayak <rnayak@ti.com> Reviewed-by: Basak, Partha <p-basak2@ti.com> Acked-by: Kevin Hilman <khilman@deeprootsystems.com> [tony@atomide.com: updated for bank specific revision and updated boards] Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-12-08 07:26:57 +07:00
postcore_initcall(omap_gpio_drv_reg);
static void __exit omap_gpio_exit(void)
{
platform_driver_unregister(&omap_gpio_driver);
}
module_exit(omap_gpio_exit);
MODULE_DESCRIPTION("omap gpio driver");
MODULE_ALIAS("platform:gpio-omap");
MODULE_LICENSE("GPL v2");