rtc: omap: Support ext_wakeup configuration

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 to
notify about power-button presses. Handling power-button presses enables
to recover from RTC-only power states correctly.

Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
Acked-by: Tony Lindgren <tony@atomide.com>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
This commit is contained in:
Marcin Niestroj 2016-09-16 11:26:28 +02:00 committed by Alexandre Belloni
parent 970fc7f4af
commit 97ea1906b3
3 changed files with 185 additions and 9 deletions

View File

@ -18,6 +18,18 @@ Optional properties:
through pmic_power_en through pmic_power_en
- clocks: Any internal or external clocks feeding in to rtc - clocks: Any internal or external clocks feeding in to rtc
- clock-names: Corresponding names of the clocks - clock-names: Corresponding names of the clocks
- pinctrl-0: a phandle pointing to the pin settings for the device
- pinctrl-names: should be "default"
Optional subnodes:
- generic pinctrl node
Required pinctrl subnodes properties:
- pins - Names of ext_wakeup pins to configure
Optional pinctrl subnodes properties:
- input-enable - Enables ext_wakeup
- ti,active-high - Set input active high (by default active low)
Example: Example:
@ -30,4 +42,13 @@ rtc@1c23000 {
system-power-controller; system-power-controller;
clocks = <&clk_32k_rtc>, <&clk_32768_ck>; clocks = <&clk_32k_rtc>, <&clk_32768_ck>;
clock-names = "ext-clk", "int-clk"; clock-names = "ext-clk", "int-clk";
pinctrl-0 = <&ext_wakeup>;
pinctrl-names = "default";
ext_wakeup: ext-wakeup {
pins = "ext_wakeup0";
input-enable;
ti,active-high;
};
}; };

View File

@ -1245,6 +1245,9 @@ config RTC_DRV_IMXDI
config RTC_DRV_OMAP config RTC_DRV_OMAP
tristate "TI OMAP Real Time Clock" tristate "TI OMAP Real Time Clock"
depends on ARCH_OMAP || ARCH_DAVINCI || COMPILE_TEST depends on ARCH_OMAP || ARCH_DAVINCI || COMPILE_TEST
depends on OF
depends on PINCTRL
select GENERIC_PINCONF
help help
Say "yes" here to support the on chip real time clock Say "yes" here to support the on chip real time clock
present on TI OMAP1, AM33xx, DA8xx/OMAP-L13x, AM43xx and DRA7xx. present on TI OMAP1, AM33xx, DA8xx/OMAP-L13x, AM43xx and DRA7xx.

View File

@ -13,19 +13,23 @@
* 2 of the License, or (at your option) any later version. * 2 of the License, or (at your option) any later version.
*/ */
#include <linux/kernel.h> #include <dt-bindings/gpio/gpio.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/rtc.h>
#include <linux/bcd.h> #include <linux/bcd.h>
#include <linux/platform_device.h> #include <linux/clk.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/io.h> #include <linux/rtc.h>
#include <linux/clk.h>
/* /*
* The OMAP RTC is a year/month/day/hours/minutes/seconds BCD clock * The OMAP RTC is a year/month/day/hours/minutes/seconds BCD clock
@ -115,6 +119,8 @@
/* OMAP_RTC_PMIC bit fields: */ /* 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_WKUP_EN(x) BIT(x)
#define OMAP_RTC_PMIC_EXT_WKUP_POL(x) BIT(4 + x)
/* OMAP_RTC_KICKER values */ /* OMAP_RTC_KICKER values */
#define KICK0_VALUE 0x83e70b13 #define KICK0_VALUE 0x83e70b13
@ -141,6 +147,7 @@ struct omap_rtc {
bool is_pmic_controller; bool is_pmic_controller;
bool has_ext_clk; bool has_ext_clk;
const struct omap_rtc_device_type *type; const struct omap_rtc_device_type *type;
struct pinctrl_dev *pctldev;
}; };
static inline u8 rtc_read(struct omap_rtc *rtc, unsigned int reg) static inline u8 rtc_read(struct omap_rtc *rtc, unsigned int reg)
@ -525,6 +532,139 @@ static const struct of_device_id omap_rtc_of_match[] = {
}; };
MODULE_DEVICE_TABLE(of, omap_rtc_of_match); MODULE_DEVICE_TABLE(of, omap_rtc_of_match);
static const struct pinctrl_pin_desc rtc_pins_desc[] = {
PINCTRL_PIN(0, "ext_wakeup0"),
PINCTRL_PIN(1, "ext_wakeup1"),
PINCTRL_PIN(2, "ext_wakeup2"),
PINCTRL_PIN(3, "ext_wakeup3"),
};
static int rtc_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
{
return 0;
}
static const char *rtc_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
unsigned int group)
{
return NULL;
}
static const struct pinctrl_ops rtc_pinctrl_ops = {
.get_groups_count = rtc_pinctrl_get_groups_count,
.get_group_name = rtc_pinctrl_get_group_name,
.dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
.dt_free_map = pinconf_generic_dt_free_map,
};
enum rtc_pin_config_param {
PIN_CONFIG_ACTIVE_HIGH = PIN_CONFIG_END + 1,
};
static const struct pinconf_generic_params rtc_params[] = {
{"ti,active-high", PIN_CONFIG_ACTIVE_HIGH, 0},
};
#ifdef CONFIG_DEBUG_FS
static const struct pin_config_item rtc_conf_items[ARRAY_SIZE(rtc_params)] = {
PCONFDUMP(PIN_CONFIG_ACTIVE_HIGH, "input active high", NULL, false),
};
#endif
static int rtc_pinconf_get(struct pinctrl_dev *pctldev,
unsigned int pin, unsigned long *config)
{
struct omap_rtc *rtc = pinctrl_dev_get_drvdata(pctldev);
unsigned int param = pinconf_to_config_param(*config);
u32 val;
u16 arg = 0;
rtc->type->unlock(rtc);
val = rtc_readl(rtc, OMAP_RTC_PMIC_REG);
rtc->type->lock(rtc);
switch (param) {
case PIN_CONFIG_INPUT_ENABLE:
if (!(val & OMAP_RTC_PMIC_EXT_WKUP_EN(pin)))
return -EINVAL;
break;
case PIN_CONFIG_ACTIVE_HIGH:
if (val & OMAP_RTC_PMIC_EXT_WKUP_POL(pin))
return -EINVAL;
break;
default:
return -ENOTSUPP;
};
*config = pinconf_to_config_packed(param, arg);
return 0;
}
static int rtc_pinconf_set(struct pinctrl_dev *pctldev,
unsigned int pin, unsigned long *configs,
unsigned int num_configs)
{
struct omap_rtc *rtc = pinctrl_dev_get_drvdata(pctldev);
u32 val;
unsigned int param;
u16 param_val;
int i;
rtc->type->unlock(rtc);
val = rtc_readl(rtc, OMAP_RTC_PMIC_REG);
rtc->type->lock(rtc);
/* active low by default */
val |= OMAP_RTC_PMIC_EXT_WKUP_POL(pin);
for (i = 0; i < num_configs; i++) {
param = pinconf_to_config_param(configs[i]);
param_val = pinconf_to_config_argument(configs[i]);
switch (param) {
case PIN_CONFIG_INPUT_ENABLE:
if (param_val)
val |= OMAP_RTC_PMIC_EXT_WKUP_EN(pin);
else
val &= ~OMAP_RTC_PMIC_EXT_WKUP_EN(pin);
break;
case PIN_CONFIG_ACTIVE_HIGH:
val &= ~OMAP_RTC_PMIC_EXT_WKUP_POL(pin);
break;
default:
dev_err(&rtc->rtc->dev, "Property %u not supported\n",
param);
return -ENOTSUPP;
}
}
rtc->type->unlock(rtc);
rtc_writel(rtc, OMAP_RTC_PMIC_REG, val);
rtc->type->lock(rtc);
return 0;
}
static const struct pinconf_ops rtc_pinconf_ops = {
.is_generic = true,
.pin_config_get = rtc_pinconf_get,
.pin_config_set = rtc_pinconf_set,
};
static struct pinctrl_desc rtc_pinctrl_desc = {
.pins = rtc_pins_desc,
.npins = ARRAY_SIZE(rtc_pins_desc),
.pctlops = &rtc_pinctrl_ops,
.confops = &rtc_pinconf_ops,
.custom_params = rtc_params,
.num_custom_params = ARRAY_SIZE(rtc_params),
#ifdef CONFIG_DEBUG_FS
.custom_conf_items = rtc_conf_items,
#endif
.owner = THIS_MODULE,
};
static int omap_rtc_probe(struct platform_device *pdev) static int omap_rtc_probe(struct platform_device *pdev)
{ {
struct omap_rtc *rtc; struct omap_rtc *rtc;
@ -681,6 +821,15 @@ static int omap_rtc_probe(struct platform_device *pdev)
} }
} }
/* Support ext_wakeup pinconf */
rtc_pinctrl_desc.name = dev_name(&pdev->dev);
rtc->pctldev = pinctrl_register(&rtc_pinctrl_desc, &pdev->dev, rtc);
if (IS_ERR(rtc->pctldev)) {
dev_err(&pdev->dev, "Couldn't register pinctrl driver\n");
return PTR_ERR(rtc->pctldev);
}
return 0; return 0;
err: err:
@ -724,6 +873,9 @@ static int __exit omap_rtc_remove(struct platform_device *pdev)
pm_runtime_put_sync(&pdev->dev); pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
/* Remove ext_wakeup pinconf */
pinctrl_unregister(rtc->pctldev);
return 0; return 0;
} }