Support configuration of ext_wakeup sources. This patch makes it possible to enable ext_wakeup (and set it's polarity), depending on board configuration. AM335x's dedicated PMIC (tps65217) uses ext_wakeup in SLEEP mode (RTC-only) to notify about power-button presses. Handling power-button presses enables to recover from RTC-only power states correctly. Implementation uses gpiochip to utilize standard bindings. However, configuration is possible only using device-tree (no runtime changes). Signed-off-by: Marcin Niestroj <m.niestroj@xxxxxxxxxxxxxxxx> --- Documentation/devicetree/bindings/rtc/rtc-omap.txt | 18 ++- drivers/rtc/Kconfig | 2 +- drivers/rtc/rtc-omap.c | 137 ++++++++++++++++++++- 3 files changed, 154 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/rtc/rtc-omap.txt b/Documentation/devicetree/bindings/rtc/rtc-omap.txt index bf7d11a..4a7738e 100644 --- a/Documentation/devicetree/bindings/rtc/rtc-omap.txt +++ b/Documentation/devicetree/bindings/rtc/rtc-omap.txt @@ -18,8 +18,12 @@ Optional properties: through pmic_power_en - clocks: Any internal or external clocks feeding in to rtc - clock-names: Corresponding names of the clocks +- gpio-controller: Mark as gpio controller when using ext_wakeup +- #gpio-cells: Should be set to 2 +- ngpios: Number of ext_wakeup sources supported by processor (board) +- ext-wakeup-gpios: List of ext_wakeup sources to configure -Example: +Examples: rtc@1c23000 { compatible = "ti,da830-rtc"; @@ -31,3 +35,15 @@ rtc@1c23000 { clocks = <&clk_32k_rtc>, <&clk_32768_ck>; clock-names = "ext-clk", "int-clk"; }; + +rtc: rtc@44e3e000 { + compatible = "ti,am3352-rtc", "ti,da830-rtc"; + reg = <0x44e3e000 0x1000>; + interrupts = <75 + 76>; + system-power-controller; + gpio-controller; + #gpio-cells = <2>; + ngpios = <1>; + ext-wakeup-gpios = <&rtc 0 GPIO_ACTIVE_LOW>; +}; diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 3e84315..f013346 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1208,7 +1208,7 @@ config RTC_DRV_IMXDI config RTC_DRV_OMAP tristate "TI OMAP Real Time Clock" - depends on ARCH_OMAP || ARCH_DAVINCI || COMPILE_TEST + depends on (ARCH_OMAP || ARCH_DAVINCI || COMPILE_TEST) && GPIOLIB help Say "yes" here to support the on chip real time clock present on TI OMAP1, AM33xx, DA8xx/OMAP-L13x, AM43xx and DRA7xx. diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index ec2e9c5..56c7155 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -26,6 +26,8 @@ #include <linux/pm_runtime.h> #include <linux/io.h> #include <linux/clk.h> +#include <linux/gpio/driver.h> +#include <linux/gpio/consumer.h> /* * The OMAP RTC is a year/month/day/hours/minutes/seconds BCD clock @@ -114,7 +116,11 @@ #define OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN BIT(1) /* OMAP_RTC_PMIC bit fields: */ -#define OMAP_RTC_PMIC_POWER_EN_EN BIT(16) +#define OMAP_RTC_PMIC_POWER_EN_EN BIT(16) +#define OMAP_RTC_PMIC_EXT_WAKEUP_EN(x) (BIT(x)) +#define OMAP_RTC_PMIC_EXT_WAKEUP_POL(x) (BIT(x) << 4) +#define OMAP_RTC_PMIC_EXT_WAKEUP_EN_MASK (0x0F) +#define OMAP_RTC_PMIC_EXT_WAKEUP_POL_MASK (0x0F << 4) /* OMAP_RTC_KICKER values */ #define KICK0_VALUE 0x83e70b13 @@ -141,6 +147,7 @@ struct omap_rtc { bool is_pmic_controller; bool has_ext_clk; const struct omap_rtc_device_type *type; + struct gpio_chip gpio_chip; }; static inline u8 rtc_read(struct omap_rtc *rtc, unsigned int reg) @@ -183,6 +190,104 @@ static void default_rtc_lock(struct omap_rtc *rtc) { } +static int omap_rtc_gpio_request(struct gpio_chip *chip, + unsigned int offset) +{ + struct omap_rtc *rtc = gpiochip_get_data(chip); + u32 val; + + rtc->type->unlock(rtc); + + val = rtc_readl(rtc, OMAP_RTC_PMIC_REG); + val |= OMAP_RTC_PMIC_EXT_WAKEUP_EN(offset); + rtc_writel(rtc, OMAP_RTC_PMIC_REG, val); + + rtc->type->lock(rtc); + + return 0; +} + +static void omap_rtc_gpio_free(struct gpio_chip *chip, + unsigned int offset) +{ + struct omap_rtc *rtc = gpiochip_get_data(chip); + u32 val; + + rtc->type->unlock(rtc); + + val = rtc_readl(rtc, OMAP_RTC_PMIC_REG); + val &= ~OMAP_RTC_PMIC_EXT_WAKEUP_EN(offset); + rtc_writel(rtc, OMAP_RTC_PMIC_REG, val); + + rtc->type->lock(rtc); +} + +static int omap_rtc_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + return 1; /* Always in */ +} + + +static int omap_rtc_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + return 0; +} + +static int omap_rtc_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + return 0; +} + +/* + * Note: This function is called only when setting ext_wakeup polarity + * with omap_rtc_gpio_set_polarity + */ +static void omap_rtc_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct omap_rtc *rtc = gpiochip_get_data(chip); + u32 val; + + rtc->type->unlock(rtc); + + val = rtc_readl(rtc, OMAP_RTC_PMIC_REG); + if (value) + val |= OMAP_RTC_PMIC_EXT_WAKEUP_POL(offset); + else + val &= ~OMAP_RTC_PMIC_EXT_WAKEUP_POL(offset); + rtc_writel(rtc, OMAP_RTC_PMIC_REG, val); + + rtc->type->lock(rtc); +} + +static void omap_rtc_gpio_set_polarity(struct gpio_descs *ext_wakeup) +{ + struct gpio_desc *desc; + int i; + + for (i = 0; i < ext_wakeup->ndescs; i++) { + desc = ext_wakeup->desc[i]; + gpiod_set_raw_value_cansleep(desc, + gpiod_is_active_low(desc)); + } +} + +static struct gpio_chip template_chip = { + .label = "omap-rtc-gpio", + .owner = THIS_MODULE, + .request = omap_rtc_gpio_request, + .free = omap_rtc_gpio_free, + .get_direction = omap_rtc_gpio_get_direction, + .direction_input = omap_rtc_gpio_direction_input, + .get = omap_rtc_gpio_get, + .set = omap_rtc_gpio_set, + .base = -1, + .ngpio = 4, + .can_sleep = true, +}; + /* * We rely on the rtc framework to handle locking (rtc->ops_lock), * so the only other requirement is that register accesses which @@ -533,6 +638,8 @@ static int omap_rtc_probe(struct platform_device *pdev) const struct platform_device_id *id_entry; const struct of_device_id *of_id; int ret; + struct gpio_descs *ext_wakeup; + u32 ngpios = 0; rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); if (!rtc) @@ -544,6 +651,10 @@ static int omap_rtc_probe(struct platform_device *pdev) rtc->is_pmic_controller = rtc->type->has_pmic_mode && of_property_read_bool(pdev->dev.of_node, "system-power-controller"); + ret = of_property_read_u32(pdev->dev.of_node, "ngpios", + &ngpios); + if (ret) + ngpios = 0; } else { id_entry = platform_get_device_id(pdev); rtc->type = (void *)id_entry->driver_data; @@ -577,6 +688,30 @@ static int omap_rtc_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); + if (ngpios > 0) { + rtc->gpio_chip = template_chip; + rtc->gpio_chip.parent = &pdev->dev; + rtc->gpio_chip.ngpio = ngpios; + ret = devm_gpiochip_add_data(&pdev->dev, &rtc->gpio_chip, + rtc); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip, %d\n", + ret); + return ret; + } + + ext_wakeup = devm_gpiod_get_array_optional(&pdev->dev, + "ext-wakeup", GPIOD_IN); + if (IS_ERR(ext_wakeup)) { + ret = PTR_ERR(ext_wakeup); + dev_err(&pdev->dev, + "ext-wakeup request failed, ret %d\n", ret); + return ret; + } + if (ext_wakeup) + omap_rtc_gpio_set_polarity(ext_wakeup); + } + rtc->type->unlock(rtc); /* -- 2.8.0 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html