Browse Source

Merge tag 'tegra-for-4.8-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux into next/drivers

soc/tegra: Changes for v4.8-rc1

Contains fixes and cleanups to the PMC driver, as well as some fixes for
the generic PM domain support and some prep work to support PCIe on 64-
bit ARM.

* tag 'tegra-for-4.8-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux:
  soc/tegra: Stub out PCIe IRQ workaround on 64-bit ARM
  soc/tegra: pmc: Enable XUSB partitions on boot
  soc/tegra: pmc: Initialise power partitions early
  soc/tegra: pmc: Add specific error messages
  soc/tegra: pmc: Use whitespace more consistently
  soc/tegra: pmc: Don't probe PMC if early initialisation fails
  soc/tegra: pmc: Add missing of_node_put()
  soc/tegra: pmc: Ensure mutex is always initialised
  soc/tegra: pmc: Don't populate SoC data until register space is mapped
  soc/tegra: pmc: Fix early initialisation of PMC
  soc/tegra: pmc: Ensure powergate is available when powering on
  soc/tegra: pmc: Initialise resets associated with a power partition
  soc/tegra: pmc: Use register definitions instead of magic values

Signed-off-by: Olof Johansson <olof@lixom.net>
Olof Johansson 9 years ago
parent
commit
358c79174a
2 changed files with 113 additions and 38 deletions
  1. 112 37
      drivers/soc/tegra/pmc.c
  2. 1 1
      include/soc/tegra/cpuidle.h

+ 112 - 37
drivers/soc/tegra/pmc.c

@@ -51,6 +51,7 @@
 #define  PMC_CNTRL_CPU_PWRREQ_POLARITY	(1 << 15)  /* CPU pwr req polarity */
 #define  PMC_CNTRL_CPU_PWRREQ_OE	(1 << 16)  /* CPU pwr req enable */
 #define  PMC_CNTRL_INTR_POLARITY	(1 << 17)  /* inverts INTR polarity */
+#define  PMC_CNTRL_MAIN_RST		(1 <<  4)
 
 #define DPD_SAMPLE			0x020
 #define  DPD_SAMPLE_ENABLE		(1 << 0)
@@ -80,6 +81,14 @@
 #define PMC_SENSOR_CTRL_SCRATCH_WRITE	(1 << 2)
 #define PMC_SENSOR_CTRL_ENABLE_RST	(1 << 1)
 
+#define PMC_RST_STATUS			0x1b4
+#define  PMC_RST_STATUS_POR		0
+#define  PMC_RST_STATUS_WATCHDOG	1
+#define  PMC_RST_STATUS_SENSOR		2
+#define  PMC_RST_STATUS_SW_MAIN		3
+#define  PMC_RST_STATUS_LP0		4
+#define  PMC_RST_STATUS_AOTAG		5
+
 #define IO_DPD_REQ			0x1b8
 #define  IO_DPD_REQ_CODE_IDLE		(0 << 30)
 #define  IO_DPD_REQ_CODE_OFF		(1 << 30)
@@ -399,6 +408,7 @@ static int tegra_powergate_power_up(struct tegra_powergate *pg,
 disable_clks:
 	tegra_powergate_disable_clocks(pg);
 	usleep_range(10, 20);
+
 powergate_off:
 	tegra_powergate_set(pg->id, false);
 
@@ -436,6 +446,7 @@ assert_resets:
 	usleep_range(10, 20);
 	tegra_powergate_reset_deassert(pg);
 	usleep_range(10, 20);
+
 disable_clks:
 	tegra_powergate_disable_clocks(pg);
 
@@ -540,6 +551,9 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk,
 	struct tegra_powergate pg;
 	int err;
 
+	if (!tegra_powergate_is_available(id))
+		return -EINVAL;
+
 	pg.id = id;
 	pg.clks = &clk;
 	pg.num_clks = 1;
@@ -638,9 +652,10 @@ static int tegra_pmc_restart_notify(struct notifier_block *this,
 
 	tegra_pmc_writel(value, PMC_SCRATCH0);
 
-	value = tegra_pmc_readl(0);
-	value |= 0x10;
-	tegra_pmc_writel(value, 0);
+	/* reset everything but PMC_SCRATCH0 and PMC_RST_STATUS */
+	value = tegra_pmc_readl(PMC_CNTRL);
+	value |= PMC_CNTRL_MAIN_RST;
+	tegra_pmc_writel(value, PMC_CNTRL);
 
 	return NOTIFY_DONE;
 }
@@ -722,13 +737,14 @@ static int tegra_powergate_of_get_clks(struct tegra_powergate *pg,
 err:
 	while (i--)
 		clk_put(pg->clks[i]);
+
 	kfree(pg->clks);
 
 	return err;
 }
 
 static int tegra_powergate_of_get_resets(struct tegra_powergate *pg,
-					 struct device_node *np)
+					 struct device_node *np, bool off)
 {
 	struct reset_control *rst;
 	unsigned int i, count;
@@ -748,6 +764,16 @@ static int tegra_powergate_of_get_resets(struct tegra_powergate *pg,
 			err = PTR_ERR(pg->resets[i]);
 			goto error;
 		}
+
+		if (off)
+			err = reset_control_assert(pg->resets[i]);
+		else
+			err = reset_control_deassert(pg->resets[i]);
+
+		if (err) {
+			reset_control_put(pg->resets[i]);
+			goto error;
+		}
 	}
 
 	pg->num_resets = count;
@@ -757,6 +783,7 @@ static int tegra_powergate_of_get_resets(struct tegra_powergate *pg,
 error:
 	while (i--)
 		reset_control_put(pg->resets[i]);
+
 	kfree(pg->resets);
 
 	return err;
@@ -765,16 +792,19 @@ error:
 static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
 {
 	struct tegra_powergate *pg;
+	int id, err;
 	bool off;
-	int id;
 
 	pg = kzalloc(sizeof(*pg), GFP_KERNEL);
 	if (!pg)
-		goto error;
+		return;
 
 	id = tegra_powergate_lookup(pmc, np->name);
-	if (id < 0)
+	if (id < 0) {
+		dev_err(pmc->dev, "powergate lookup failed for %s: %d\n",
+			np->name, id);
 		goto free_mem;
+	}
 
 	/*
 	 * Clear the bit for this powergate so it cannot be managed
@@ -788,31 +818,64 @@ static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
 	pg->genpd.power_on = tegra_genpd_power_on;
 	pg->pmc = pmc;
 
-	if (tegra_powergate_of_get_clks(pg, np))
+	off = !tegra_powergate_is_powered(pg->id);
+
+	err = tegra_powergate_of_get_clks(pg, np);
+	if (err < 0) {
+		dev_err(pmc->dev, "failed to get clocks for %s: %d\n",
+			np->name, err);
 		goto set_available;
+	}
 
-	if (tegra_powergate_of_get_resets(pg, np))
+	err = tegra_powergate_of_get_resets(pg, np, off);
+	if (err < 0) {
+		dev_err(pmc->dev, "failed to get resets for %s: %d\n",
+			np->name, err);
 		goto remove_clks;
+	}
 
-	off = !tegra_powergate_is_powered(pg->id);
+	if (!IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS))
+		goto power_on_cleanup;
+
+	/*
+	 * FIXME: If XHCI is enabled for Tegra, then power-up the XUSB
+	 * host and super-speed partitions. Once the XHCI driver
+	 * manages the partitions itself this code can be removed. Note
+	 * that we don't register these partitions with the genpd core
+	 * to avoid it from powering down the partitions as they appear
+	 * to be unused.
+	 */
+	if (IS_ENABLED(CONFIG_USB_XHCI_TEGRA) &&
+	    (id == TEGRA_POWERGATE_XUSBA || id == TEGRA_POWERGATE_XUSBC))
+		goto power_on_cleanup;
 
 	pm_genpd_init(&pg->genpd, NULL, off);
 
-	if (of_genpd_add_provider_simple(np, &pg->genpd))
+	err = of_genpd_add_provider_simple(np, &pg->genpd);
+	if (err < 0) {
+		dev_err(pmc->dev, "failed to add genpd provider for %s: %d\n",
+			np->name, err);
 		goto remove_resets;
+	}
 
 	dev_dbg(pmc->dev, "added power domain %s\n", pg->genpd.name);
 
 	return;
 
+power_on_cleanup:
+	if (off)
+		WARN_ON(tegra_powergate_power_up(pg, true));
+
 remove_resets:
 	while (pg->num_resets--)
 		reset_control_put(pg->resets[pg->num_resets]);
+
 	kfree(pg->resets);
 
 remove_clks:
 	while (pg->num_clks--)
 		clk_put(pg->clks[pg->num_clks]);
+
 	kfree(pg->clks);
 
 set_available:
@@ -820,16 +883,20 @@ set_available:
 
 free_mem:
 	kfree(pg);
-
-error:
-	dev_err(pmc->dev, "failed to create power domain for %s\n", np->name);
 }
 
-static void tegra_powergate_init(struct tegra_pmc *pmc)
+static void tegra_powergate_init(struct tegra_pmc *pmc,
+				 struct device_node *parent)
 {
 	struct device_node *np, *child;
+	unsigned int i;
+
+	/* Create a bitmap of the available and valid partitions */
+	for (i = 0; i < pmc->soc->num_powergates; i++)
+		if (pmc->soc->powergates[i])
+			set_bit(i, pmc->powergates_available);
 
-	np = of_get_child_by_name(pmc->dev->of_node, "powergates");
+	np = of_get_child_by_name(parent, "powergates");
 	if (!np)
 		return;
 
@@ -1205,6 +1272,14 @@ static int tegra_pmc_probe(struct platform_device *pdev)
 	struct resource *res;
 	int err;
 
+	/*
+	 * Early initialisation should have configured an initial
+	 * register mapping and setup the soc data pointer. If these
+	 * are not valid then something went badly wrong!
+	 */
+	if (WARN_ON(!pmc->base || !pmc->soc))
+		return -ENODEV;
+
 	err = tegra_pmc_parse_dt(pmc, pdev->dev.of_node);
 	if (err < 0)
 		return err;
@@ -1242,8 +1317,6 @@ static int tegra_pmc_probe(struct platform_device *pdev)
 		return err;
 	}
 
-	tegra_powergate_init(pmc);
-
 	mutex_lock(&pmc->powergates_lock);
 	iounmap(pmc->base);
 	pmc->base = base;
@@ -1477,10 +1550,11 @@ static int __init tegra_pmc_early_init(void)
 	const struct of_device_id *match;
 	struct device_node *np;
 	struct resource regs;
-	unsigned int i;
 	bool invert;
 	u32 value;
 
+	mutex_init(&pmc->powergates_lock);
+
 	np = of_find_matching_node_and_match(NULL, tegra_pmc_match, &match);
 	if (!np) {
 		/*
@@ -1515,39 +1589,40 @@ static int __init tegra_pmc_early_init(void)
 		 */
 		if (of_address_to_resource(np, 0, &regs) < 0) {
 			pr_err("failed to get PMC registers\n");
+			of_node_put(np);
 			return -ENXIO;
 		}
-
-		pmc->soc = match->data;
 	}
 
 	pmc->base = ioremap_nocache(regs.start, resource_size(&regs));
 	if (!pmc->base) {
 		pr_err("failed to map PMC registers\n");
+		of_node_put(np);
 		return -ENXIO;
 	}
 
-	/* Create a bit-map of the available and valid partitions */
-	for (i = 0; i < pmc->soc->num_powergates; i++)
-		if (pmc->soc->powergates[i])
-			set_bit(i, pmc->powergates_available);
+	if (np) {
+		pmc->soc = match->data;
 
-	mutex_init(&pmc->powergates_lock);
+		tegra_powergate_init(pmc, np);
 
-	/*
-	 * Invert the interrupt polarity if a PMC device tree node exists and
-	 * contains the nvidia,invert-interrupt property.
-	 */
-	invert = of_property_read_bool(np, "nvidia,invert-interrupt");
+		/*
+		 * Invert the interrupt polarity if a PMC device tree node
+		 * exists and contains the nvidia,invert-interrupt property.
+		 */
+		invert = of_property_read_bool(np, "nvidia,invert-interrupt");
 
-	value = tegra_pmc_readl(PMC_CNTRL);
+		value = tegra_pmc_readl(PMC_CNTRL);
 
-	if (invert)
-		value |= PMC_CNTRL_INTR_POLARITY;
-	else
-		value &= ~PMC_CNTRL_INTR_POLARITY;
+		if (invert)
+			value |= PMC_CNTRL_INTR_POLARITY;
+		else
+			value &= ~PMC_CNTRL_INTR_POLARITY;
 
-	tegra_pmc_writel(value, PMC_CNTRL);
+		tegra_pmc_writel(value, PMC_CNTRL);
+
+		of_node_put(np);
+	}
 
 	return 0;
 }

+ 1 - 1
include/soc/tegra/cpuidle.h

@@ -14,7 +14,7 @@
 #ifndef __SOC_TEGRA_CPUIDLE_H__
 #define __SOC_TEGRA_CPUIDLE_H__
 
-#ifdef CONFIG_CPU_IDLE
+#if defined(CONFIG_ARM) && defined(CONFIG_CPU_IDLE)
 void tegra_cpuidle_pcie_irqs_in_use(void);
 #else
 static inline void tegra_cpuidle_pcie_irqs_in_use(void)