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 | 187 ++++++++++++++++++++- 3 files changed, 204 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..e21e229 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -16,6 +16,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> +#include <linux/slab.h> #include <linux/ioport.h> #include <linux/delay.h> #include <linux/rtc.h> @@ -23,6 +24,8 @@ #include <linux/platform_device.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/gpio/driver.h> #include <linux/pm_runtime.h> #include <linux/io.h> #include <linux/clk.h> @@ -114,7 +117,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 @@ -131,6 +138,17 @@ struct omap_rtc_device_type { void (*unlock)(struct omap_rtc *rtc); }; +struct omap_rtc_gpio_desc { + struct gpio_desc *desc; + int hwnum; + bool active_low; +}; + +struct omap_rtc_gpio_descs { + unsigned int ndescs; + struct omap_rtc_gpio_desc desc[]; +}; + struct omap_rtc { struct rtc_device *rtc; void __iomem *base; @@ -141,6 +159,8 @@ struct omap_rtc { bool is_pmic_controller; bool has_ext_clk; const struct omap_rtc_device_type *type; + struct gpio_chip gpio_chip; + struct omap_rtc_gpio_descs *descs; }; static inline u8 rtc_read(struct omap_rtc *rtc, unsigned int reg) @@ -183,6 +203,135 @@ static void default_rtc_lock(struct omap_rtc *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; +} + +static struct gpio_chip template_chip = { + .label = "omap-rtc-gpio", + .owner = THIS_MODULE, + .get_direction = omap_rtc_gpio_get_direction, + .direction_input = omap_rtc_gpio_direction_input, + .get = omap_rtc_gpio_get, + .base = -1, + .ngpio = 4, + .can_sleep = true, +}; + +static void omap_rtc_gpio_enable(struct omap_rtc *rtc) +{ + struct omap_rtc_gpio_descs *descs = rtc->descs; + struct omap_rtc_gpio_desc *desc; + u32 val; + unsigned int i; + + if (!descs) + return; + + val = rtc_readl(rtc, OMAP_RTC_PMIC_REG); + val &= ~(OMAP_RTC_PMIC_EXT_WAKEUP_EN_MASK + | OMAP_RTC_PMIC_EXT_WAKEUP_POL_MASK); + + for (i = 0; i < descs->ndescs; i++) { + desc = &descs->desc[i]; + val |= OMAP_RTC_PMIC_EXT_WAKEUP_EN(desc->hwnum); + if (desc->active_low) + val |= OMAP_RTC_PMIC_EXT_WAKEUP_POL(desc->hwnum); + } + + rtc_writel(rtc, OMAP_RTC_PMIC_REG, val); +} + +static void omap_rtc_gpio_disable(struct omap_rtc *rtc) +{ + u32 val; + + val = rtc_readl(rtc, OMAP_RTC_PMIC_REG); + val &= ~(OMAP_RTC_PMIC_EXT_WAKEUP_EN_MASK + | OMAP_RTC_PMIC_EXT_WAKEUP_POL_MASK); + rtc_writel(rtc, OMAP_RTC_PMIC_REG, val); +} + +static void omap_rtc_gpio_descs_put(struct omap_rtc_gpio_descs *descs) +{ + unsigned int i; + + for (i = 0; i < descs->ndescs; i++) + gpiochip_free_own_desc(descs->desc[i].desc); + + kfree(descs); +} + +static struct omap_rtc_gpio_descs *omap_rtc_gpio_descs_get(struct device *dev) +{ + struct omap_rtc *rtc = dev_get_drvdata(dev); + struct device_node *np = dev->of_node; + struct gpio_chip *chip = &rtc->gpio_chip; + struct of_phandle_args of_args; + enum of_gpio_flags of_flags; + struct omap_rtc_gpio_descs *descs; + struct omap_rtc_gpio_desc *desc; + int index; + int count; + int ret; + + count = of_gpio_named_count(np, "ext-wakeup-gpios"); + if (count <= 0) + return ERR_PTR(-ENOENT); + + descs = kzalloc(sizeof(*descs) + sizeof(descs->desc[0]) * count, + GFP_KERNEL); + if (!descs) + return ERR_PTR(-ENOMEM); + + for (index = 0; index < count; index++) { + desc = &descs->desc[index]; + ret = of_parse_phandle_with_args(np, "ext-wakeup-gpios", + "#gpio-cells", index, + &of_args); + if (ret) { + dev_err(dev, "Could not parse ext-wakeup-gpios %d\n", + index); + omap_rtc_gpio_descs_put(descs); + return ERR_PTR(ret); + } + + ret = of_gpio_simple_xlate(chip, &of_args, &of_flags); + if (ret < 0) { + dev_err(dev, "Could not xlate %d\n", index); + omap_rtc_gpio_descs_put(descs); + return ERR_PTR(ret); + } + desc->hwnum = ret; + + desc->desc = gpiochip_request_own_desc(chip, ret, + "omap-rtc-gpio"); + if (IS_ERR(desc->desc)) { + dev_err(dev, "Could not request gpio %d\n", index); + omap_rtc_gpio_descs_put(descs); + return ERR_CAST(desc->desc); + } + + desc->active_low = (of_flags & OF_GPIO_ACTIVE_LOW); + descs->ndescs++; + } + + return descs; +} + /* * We rely on the rtc framework to handle locking (rtc->ops_lock), * so the only other requirement is that register accesses which @@ -532,6 +681,8 @@ static int omap_rtc_probe(struct platform_device *pdev) u8 reg, mask, new_ctrl; const struct platform_device_id *id_entry; const struct of_device_id *of_id; + struct omap_rtc_gpio_descs *descs = NULL; + u32 ngpios = 0; int ret; rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); @@ -544,6 +695,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 +732,26 @@ 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; + } + + descs = omap_rtc_gpio_descs_get(&pdev->dev); + if (IS_ERR(descs)) { + dev_err(&pdev->dev, "Could not request gpios\n"); + return PTR_ERR(descs); + } + rtc->descs = descs; + } + rtc->type->unlock(rtc); /* @@ -619,6 +794,8 @@ static int omap_rtc_probe(struct platform_device *pdev) new_ctrl = reg & (OMAP_RTC_CTRL_SPLIT | OMAP_RTC_CTRL_AUTO_COMP); new_ctrl |= OMAP_RTC_CTRL_STOP; + omap_rtc_gpio_enable(rtc); + /* * BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE: * @@ -684,6 +861,8 @@ static int omap_rtc_probe(struct platform_device *pdev) return 0; err: + if (descs) + omap_rtc_gpio_descs_put(descs); device_init_wakeup(&pdev->dev, false); rtc->type->lock(rtc); pm_runtime_put_sync(&pdev->dev); @@ -697,6 +876,9 @@ static int __exit omap_rtc_remove(struct platform_device *pdev) struct omap_rtc *rtc = platform_get_drvdata(pdev); u8 reg; + if (rtc->descs) + omap_rtc_gpio_descs_put(rtc->descs); + if (pm_power_off == omap_rtc_power_off && omap_rtc_power_off_rtc == rtc) { pm_power_off = NULL; @@ -709,6 +891,9 @@ static int __exit omap_rtc_remove(struct platform_device *pdev) clk_disable_unprepare(rtc->clk); rtc->type->unlock(rtc); + + omap_rtc_gpio_disable(rtc); + /* leave rtc running, but disable irqs */ rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, 0); -- 2.8.0 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html