summaryrefslogtreecommitdiff
path: root/drivers/soc
diff options
context:
space:
mode:
authorPetlozu Pravareshwar <petlozup@nvidia.com>2022-09-30 16:14:52 +0000
committerThierry Reding <treding@nvidia.com>2022-11-10 02:48:05 +0100
commit1ddb8f6d44ff482c9953a06f800453bc372cfead (patch)
treea0bc82021b9609176c250d37fd77ca3825c0d72a /drivers/soc
parentc9c4ddb20c427b19c6a2a1787bf82c7b2aac25c3 (diff)
soc/tegra: pmc: Fix dual edge triggered wakes
When a wake event is defined to be triggered on both positive and negative edge of the input wake signal, it is crucial to know the current state of the signal when going into suspend. The intended way to obtain the current state of the wake signals is to read the WAKE_AOWAKE_SW_STATUS register, which should contains the raw state of the wake signals. However, this register is edge triggered, an edge will not be generated for signals that are already asserted prior to the assertion of WAKE_LATCH_SW. To workaround this, change the polarity of the wake level from '0' to '1' while latching the signals, as this will generate an edge for signals that are set to '1'. Signed-off-by: Stefan Kristiansson <stefank@nvidia.com> Signed-off-by: Petlozu Pravareshwar <petlozup@nvidia.com> Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/soc')
-rw-r--r--drivers/soc/tegra/pmc.c181
1 files changed, 176 insertions, 5 deletions
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 9a195c1e84a0..767f33303617 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -46,6 +46,7 @@
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/syscore_ops.h>
#include <soc/tegra/common.h>
#include <soc/tegra/fuse.h>
@@ -182,6 +183,9 @@
#define WAKE_AOWAKE_TIER0_ROUTING(x) (0x4b4 + ((x) << 2))
#define WAKE_AOWAKE_TIER1_ROUTING(x) (0x4c0 + ((x) << 2))
#define WAKE_AOWAKE_TIER2_ROUTING(x) (0x4cc + ((x) << 2))
+#define WAKE_AOWAKE_SW_STATUS_W_0 0x49c
+#define WAKE_AOWAKE_SW_STATUS(x) (0x4a0 + ((x) << 2))
+#define WAKE_LATCH_SW 0x498
#define WAKE_AOWAKE_CTRL 0x4f4
#define WAKE_AOWAKE_CTRL_INTR_POLARITY BIT(0)
@@ -367,6 +371,8 @@ struct tegra_pmc_soc {
*/
const struct tegra_wake_event *wake_events;
unsigned int num_wake_events;
+ unsigned int max_wake_events;
+ unsigned int max_wake_vectors;
const struct pmc_clk_init_data *pmc_clks_data;
unsigned int num_pmc_clks;
@@ -407,6 +413,11 @@ struct tegra_pmc_soc {
* @clk_nb: pclk clock changes handler
* @core_domain_state_synced: flag marking the core domain's state as synced
* @core_domain_registered: flag marking the core domain as registered
+ * @wake_type_level_map: Bitmap indicating level type for non-dual edge wakes
+ * @wake_type_dual_edge_map: Bitmap indicating if a wake is dual-edge or not
+ * @wake_sw_status_map: Bitmap to hold raw status of wakes without mask
+ * @wake_cntrl_level_map: Bitmap to hold wake levels to be programmed in
+ * cntrl register associated with each wake during system suspend.
*/
struct tegra_pmc {
struct device *dev;
@@ -447,6 +458,12 @@ struct tegra_pmc {
bool core_domain_state_synced;
bool core_domain_registered;
+
+ unsigned long *wake_type_level_map;
+ unsigned long *wake_type_dual_edge_map;
+ unsigned long *wake_sw_status_map;
+ unsigned long *wake_cntrl_level_map;
+ struct syscore_ops syscore;
};
static struct tegra_pmc *pmc = &(struct tegra_pmc) {
@@ -1923,10 +1940,30 @@ static int tegra_pmc_parse_dt(struct tegra_pmc *pmc, struct device_node *np)
return 0;
}
-static void tegra_pmc_init(struct tegra_pmc *pmc)
+static int tegra_pmc_init(struct tegra_pmc *pmc)
{
+ if (pmc->soc->max_wake_events > 0) {
+ pmc->wake_type_level_map = bitmap_zalloc(pmc->soc->max_wake_events, GFP_KERNEL);
+ if (!pmc->wake_type_level_map)
+ return -ENOMEM;
+
+ pmc->wake_type_dual_edge_map = bitmap_zalloc(pmc->soc->max_wake_events, GFP_KERNEL);
+ if (!pmc->wake_type_dual_edge_map)
+ return -ENOMEM;
+
+ pmc->wake_sw_status_map = bitmap_zalloc(pmc->soc->max_wake_events, GFP_KERNEL);
+ if (!pmc->wake_sw_status_map)
+ return -ENOMEM;
+
+ pmc->wake_cntrl_level_map = bitmap_zalloc(pmc->soc->max_wake_events, GFP_KERNEL);
+ if (!pmc->wake_cntrl_level_map)
+ return -ENOMEM;
+ }
+
if (pmc->soc->init)
pmc->soc->init(pmc);
+
+ return 0;
}
static void tegra_pmc_init_tsense_reset(struct tegra_pmc *pmc)
@@ -2417,15 +2454,21 @@ static int tegra186_pmc_irq_set_type(struct irq_data *data, unsigned int type)
case IRQ_TYPE_EDGE_RISING:
case IRQ_TYPE_LEVEL_HIGH:
value |= WAKE_AOWAKE_CNTRL_LEVEL;
+ set_bit(data->hwirq, pmc->wake_type_level_map);
+ clear_bit(data->hwirq, pmc->wake_type_dual_edge_map);
break;
case IRQ_TYPE_EDGE_FALLING:
case IRQ_TYPE_LEVEL_LOW:
value &= ~WAKE_AOWAKE_CNTRL_LEVEL;
+ clear_bit(data->hwirq, pmc->wake_type_level_map);
+ clear_bit(data->hwirq, pmc->wake_type_dual_edge_map);
break;
case IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING:
value ^= WAKE_AOWAKE_CNTRL_LEVEL;
+ clear_bit(data->hwirq, pmc->wake_type_level_map);
+ set_bit(data->hwirq, pmc->wake_type_dual_edge_map);
break;
default:
@@ -2957,7 +3000,11 @@ static int tegra_pmc_probe(struct platform_device *pdev)
pmc->dev = &pdev->dev;
- tegra_pmc_init(pmc);
+ err = tegra_pmc_init(pmc);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to initialize PMC: %d\n", err);
+ return err;
+ }
tegra_pmc_init_tsense_reset(pmc);
@@ -3008,6 +3055,117 @@ cleanup_sysfs:
return err;
}
+/*
+ * Ensures that sufficient time is passed for a register write to
+ * serialize into the 32KHz domain.
+ */
+static void wke_32kwritel(struct tegra_pmc *pmc, u32 value, unsigned int offset)
+{
+ writel(value, pmc->wake + offset);
+ udelay(130);
+}
+
+static void wke_write_wake_level(struct tegra_pmc *pmc, int wake, int level)
+{
+ unsigned int offset = WAKE_AOWAKE_CNTRL(wake);
+ u32 value;
+
+ value = readl(pmc->wake + offset);
+ if (level)
+ value |= WAKE_AOWAKE_CNTRL_LEVEL;
+ else
+ value &= ~WAKE_AOWAKE_CNTRL_LEVEL;
+
+ writel(value, pmc->wake + offset);
+}
+
+static void wke_write_wake_levels(struct tegra_pmc *pmc)
+{
+ unsigned int i;
+
+ for (i = 0; i < pmc->soc->max_wake_events; i++)
+ wke_write_wake_level(pmc, i, test_bit(i, pmc->wake_cntrl_level_map));
+}
+
+static void wke_clear_sw_wake_status(struct tegra_pmc *pmc)
+{
+ wke_32kwritel(pmc, 1, WAKE_AOWAKE_SW_STATUS_W_0);
+}
+
+static void wke_read_sw_wake_status(struct tegra_pmc *pmc)
+{
+ unsigned long status;
+ unsigned int wake, i;
+
+ for (i = 0; i < pmc->soc->max_wake_events; i++)
+ wke_write_wake_level(pmc, i, 0);
+
+ wke_clear_sw_wake_status(pmc);
+
+ wke_32kwritel(pmc, 1, WAKE_LATCH_SW);
+
+ /*
+ * WAKE_AOWAKE_SW_STATUS is edge triggered, so in order to
+ * obtain the current status of the input wake signals, change
+ * the polarity of the wake level from 0->1 while latching to force
+ * a positive edge if the sampled signal is '1'.
+ */
+ for (i = 0; i < pmc->soc->max_wake_events; i++)
+ wke_write_wake_level(pmc, i, 1);
+
+ /*
+ * Wait for the update to be synced into the 32kHz domain,
+ * and let enough time lapse, so that the wake signals have time to
+ * be sampled.
+ */
+ udelay(300);
+
+ wke_32kwritel(pmc, 0, WAKE_LATCH_SW);
+
+ bitmap_zero(pmc->wake_sw_status_map, pmc->soc->max_wake_events);
+
+ for (i = 0; i < pmc->soc->max_wake_vectors; i++) {
+ status = readl(pmc->wake + WAKE_AOWAKE_SW_STATUS(i));
+
+ for_each_set_bit(wake, &status, 32)
+ set_bit(wake + (i * 32), pmc->wake_sw_status_map);
+ }
+}
+
+static void wke_clear_wake_status(struct tegra_pmc *pmc)
+{
+ unsigned long status;
+ unsigned int i, wake;
+ u32 mask;
+
+ for (i = 0; i < pmc->soc->max_wake_vectors; i++) {
+ mask = readl(pmc->wake + WAKE_AOWAKE_TIER2_ROUTING(i));
+ status = readl(pmc->wake + WAKE_AOWAKE_STATUS_R(i)) & mask;
+
+ for_each_set_bit(wake, &status, 32)
+ wke_32kwritel(pmc, 0x1, WAKE_AOWAKE_STATUS_W((i * 32) + wake));
+ }
+}
+
+static int tegra186_pmc_wake_syscore_suspend(void)
+{
+ wke_read_sw_wake_status(pmc);
+
+ /* flip the wakeup trigger for dual-edge triggered pads
+ * which are currently asserting as wakeups
+ */
+ bitmap_andnot(pmc->wake_cntrl_level_map, pmc->wake_type_dual_edge_map,
+ pmc->wake_sw_status_map, pmc->soc->max_wake_events);
+ bitmap_or(pmc->wake_cntrl_level_map, pmc->wake_cntrl_level_map,
+ pmc->wake_type_level_map, pmc->soc->max_wake_events);
+
+ /* Clear PMC Wake Status registers while going to suspend */
+ wke_clear_wake_status(pmc);
+ wke_write_wake_levels(pmc);
+
+ return 0;
+}
+
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_ARM)
static int tegra_pmc_suspend(struct device *dev)
{
@@ -3651,6 +3809,13 @@ static const struct tegra_pmc_regs tegra186_pmc_regs = {
.rst_level_mask = 0x3,
};
+static void tegra186_pmc_init(struct tegra_pmc *pmc)
+{
+ pmc->syscore.suspend = tegra186_pmc_wake_syscore_suspend;
+
+ register_syscore_ops(&pmc->syscore);
+}
+
static void tegra186_pmc_setup_irq_polarity(struct tegra_pmc *pmc,
struct device_node *np,
bool invert)
@@ -3730,7 +3895,7 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = {
.num_pin_descs = ARRAY_SIZE(tegra186_pin_descs),
.pin_descs = tegra186_pin_descs,
.regs = &tegra186_pmc_regs,
- .init = NULL,
+ .init = tegra186_pmc_init,
.setup_irq_polarity = tegra186_pmc_setup_irq_polarity,
.irq_set_wake = tegra186_pmc_irq_set_wake,
.irq_set_type = tegra186_pmc_irq_set_type,
@@ -3740,6 +3905,8 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = {
.num_reset_levels = ARRAY_SIZE(tegra186_reset_levels),
.num_wake_events = ARRAY_SIZE(tegra186_wake_events),
.wake_events = tegra186_wake_events,
+ .max_wake_events = 96,
+ .max_wake_vectors = 3,
.pmc_clks_data = NULL,
.num_pmc_clks = 0,
.has_blink_output = false,
@@ -3912,7 +4079,7 @@ static const struct tegra_pmc_soc tegra194_pmc_soc = {
.num_pin_descs = ARRAY_SIZE(tegra194_pin_descs),
.pin_descs = tegra194_pin_descs,
.regs = &tegra194_pmc_regs,
- .init = NULL,
+ .init = tegra186_pmc_init,
.setup_irq_polarity = tegra186_pmc_setup_irq_polarity,
.irq_set_wake = tegra186_pmc_irq_set_wake,
.irq_set_type = tegra186_pmc_irq_set_type,
@@ -3922,6 +4089,8 @@ static const struct tegra_pmc_soc tegra194_pmc_soc = {
.num_reset_levels = ARRAY_SIZE(tegra186_reset_levels),
.num_wake_events = ARRAY_SIZE(tegra194_wake_events),
.wake_events = tegra194_wake_events,
+ .max_wake_events = 96,
+ .max_wake_vectors = 3,
.pmc_clks_data = NULL,
.num_pmc_clks = 0,
.has_blink_output = false,
@@ -4035,7 +4204,7 @@ static const struct tegra_pmc_soc tegra234_pmc_soc = {
.num_pin_descs = ARRAY_SIZE(tegra234_pin_descs),
.pin_descs = tegra234_pin_descs,
.regs = &tegra234_pmc_regs,
- .init = NULL,
+ .init = tegra186_pmc_init,
.setup_irq_polarity = tegra186_pmc_setup_irq_polarity,
.irq_set_wake = tegra186_pmc_irq_set_wake,
.irq_set_type = tegra186_pmc_irq_set_type,
@@ -4045,6 +4214,8 @@ static const struct tegra_pmc_soc tegra234_pmc_soc = {
.num_reset_levels = ARRAY_SIZE(tegra186_reset_levels),
.num_wake_events = ARRAY_SIZE(tegra234_wake_events),
.wake_events = tegra234_wake_events,
+ .max_wake_events = 96,
+ .max_wake_vectors = 3,
.pmc_clks_data = NULL,
.num_pmc_clks = 0,
.has_blink_output = false,