@@ -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)
@@ -356,6 +360,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;
@@ -396,6 +402,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;
@@ -436,6 +447,11 @@ 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;
};
static struct tegra_pmc *pmc = &(struct tegra_pmc) {
@@ -1913,10 +1929,35 @@ 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 = kcalloc(BITS_TO_LONGS(
+ pmc->soc->max_wake_events),
+ sizeof(unsigned long), GFP_KERNEL);
+ if (!pmc->wake_type_level_map)
+ return -ENOMEM;
+ pmc->wake_type_dual_edge_map = kcalloc(BITS_TO_LONGS(
+ pmc->soc->max_wake_events),
+ sizeof(unsigned long), GFP_KERNEL);
+ if (!pmc->wake_type_dual_edge_map)
+ return -ENOMEM;
+ pmc->wake_sw_status_map = kcalloc(BITS_TO_LONGS(
+ pmc->soc->max_wake_events),
+ sizeof(unsigned long), GFP_KERNEL);
+ if (!pmc->wake_sw_status_map)
+ return -ENOMEM;
+ pmc->wake_cntrl_level_map = kcalloc(BITS_TO_LONGS(
+ pmc->soc->max_wake_events),
+ sizeof(unsigned long), 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)
@@ -2399,15 +2440,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:
@@ -2946,7 +2993,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) {
+ dev_err(&pdev->dev, "failed to init pmc: %d\n", err);
+ return err;
+ }
tegra_pmc_init_tsense_reset(pmc);
@@ -2997,6 +3048,118 @@ static int tegra_pmc_probe(struct platform_device *pdev)
return err;
}
+/*
+ * Ensures that sufficient time is passed for a register write to
+ * serialize into the 32KHz domain.
+ */
+static void wke_32kwritel(u32 val, u32 reg)
+{
+ writel(val, pmc->wake + reg);
+ udelay(130);
+}
+
+static void wke_write_wake_level(int wake, int level)
+{
+ u32 val;
+ u32 reg = WAKE_AOWAKE_CNTRL(wake);
+
+ val = readl(pmc->wake + reg);
+ if (level)
+ val |= WAKE_AOWAKE_CNTRL_LEVEL;
+ else
+ val &= ~WAKE_AOWAKE_CNTRL_LEVEL;
+ writel(val, pmc->wake + reg);
+}
+
+static void wke_write_wake_levels(void)
+{
+ int i;
+
+ for (i = 0; i < pmc->soc->max_wake_events; i++)
+ wke_write_wake_level(i, test_bit(i, pmc->wake_cntrl_level_map));
+}
+
+static void wke_clear_sw_wake_status(void)
+{
+ wke_32kwritel(1, WAKE_AOWAKE_SW_STATUS_W_0);
+}
+
+static void wke_read_sw_wake_status(void)
+{
+ unsigned long status;
+ int wake, i;
+
+ for (i = 0; i < pmc->soc->max_wake_events; i++)
+ wke_write_wake_level(i, 0);
+
+ wke_clear_sw_wake_status();
+
+ wke_32kwritel(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(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(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 = (unsigned long)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(void)
+{
+ u32 status;
+ int i, wake;
+ unsigned long ulong_status;
+
+ for (i = 0; i < pmc->soc->max_wake_vectors; i++) {
+ status = readl(pmc->wake + WAKE_AOWAKE_STATUS_R(i));
+ status = status & readl(pmc->wake +
+ WAKE_AOWAKE_TIER2_ROUTING(i));
+ ulong_status = (unsigned long)status;
+ for_each_set_bit(wake, &ulong_status, 32)
+ wke_32kwritel(0x1,
+ WAKE_AOWAKE_STATUS_W((i * 32) + wake));
+ }
+}
+
+static int tegra186_pmc_wake_syscore_suspend(void)
+{
+ wke_read_sw_wake_status();
+
+ /* 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();
+
+ wke_write_wake_levels();
+
+ return 0;
+}
+
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_ARM)
static int tegra_pmc_suspend(struct device *dev)
{
@@ -3652,6 +3815,15 @@ static const struct tegra_pmc_regs tegra186_pmc_regs = {
.rst_level_mask = 0x3,
};
+static struct syscore_ops tegra186_pmc_wake_syscore_ops = {
+ .suspend = tegra186_pmc_wake_syscore_suspend,
+};
+
+static void tegra186_pmc_init(struct tegra_pmc *pmc)
+{
+ register_syscore_ops(&tegra186_pmc_wake_syscore_ops);
+}
+
static void tegra186_pmc_setup_irq_polarity(struct tegra_pmc *pmc,
struct device_node *np,
bool invert)
@@ -3731,7 +3903,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,
@@ -3741,6 +3913,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,
@@ -3910,7 +4084,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,
@@ -3920,6 +4094,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,
@@ -4037,7 +4213,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,
@@ -4047,6 +4223,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,