Commit 358c7917 authored by Olof Johansson's avatar Olof Johansson

Merge tag 'tegra-for-4.8-soc' of...

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: default avatarOlof Johansson <olof@lixom.net>
parents e1d1dfc2 8582f6d1
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
#define PMC_CNTRL_CPU_PWRREQ_POLARITY (1 << 15) /* CPU pwr req polarity */ #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_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */
#define PMC_CNTRL_INTR_POLARITY (1 << 17) /* inverts INTR polarity */ #define PMC_CNTRL_INTR_POLARITY (1 << 17) /* inverts INTR polarity */
#define PMC_CNTRL_MAIN_RST (1 << 4)
#define DPD_SAMPLE 0x020 #define DPD_SAMPLE 0x020
#define DPD_SAMPLE_ENABLE (1 << 0) #define DPD_SAMPLE_ENABLE (1 << 0)
...@@ -80,6 +81,14 @@ ...@@ -80,6 +81,14 @@
#define PMC_SENSOR_CTRL_SCRATCH_WRITE (1 << 2) #define PMC_SENSOR_CTRL_SCRATCH_WRITE (1 << 2)
#define PMC_SENSOR_CTRL_ENABLE_RST (1 << 1) #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 0x1b8
#define IO_DPD_REQ_CODE_IDLE (0 << 30) #define IO_DPD_REQ_CODE_IDLE (0 << 30)
#define IO_DPD_REQ_CODE_OFF (1 << 30) #define IO_DPD_REQ_CODE_OFF (1 << 30)
...@@ -399,6 +408,7 @@ static int tegra_powergate_power_up(struct tegra_powergate *pg, ...@@ -399,6 +408,7 @@ static int tegra_powergate_power_up(struct tegra_powergate *pg,
disable_clks: disable_clks:
tegra_powergate_disable_clocks(pg); tegra_powergate_disable_clocks(pg);
usleep_range(10, 20); usleep_range(10, 20);
powergate_off: powergate_off:
tegra_powergate_set(pg->id, false); tegra_powergate_set(pg->id, false);
...@@ -436,6 +446,7 @@ static int tegra_powergate_power_down(struct tegra_powergate *pg) ...@@ -436,6 +446,7 @@ static int tegra_powergate_power_down(struct tegra_powergate *pg)
usleep_range(10, 20); usleep_range(10, 20);
tegra_powergate_reset_deassert(pg); tegra_powergate_reset_deassert(pg);
usleep_range(10, 20); usleep_range(10, 20);
disable_clks: disable_clks:
tegra_powergate_disable_clocks(pg); tegra_powergate_disable_clocks(pg);
...@@ -540,6 +551,9 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk, ...@@ -540,6 +551,9 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk,
struct tegra_powergate pg; struct tegra_powergate pg;
int err; int err;
if (!tegra_powergate_is_available(id))
return -EINVAL;
pg.id = id; pg.id = id;
pg.clks = &clk; pg.clks = &clk;
pg.num_clks = 1; pg.num_clks = 1;
...@@ -638,9 +652,10 @@ static int tegra_pmc_restart_notify(struct notifier_block *this, ...@@ -638,9 +652,10 @@ static int tegra_pmc_restart_notify(struct notifier_block *this,
tegra_pmc_writel(value, PMC_SCRATCH0); tegra_pmc_writel(value, PMC_SCRATCH0);
value = tegra_pmc_readl(0); /* reset everything but PMC_SCRATCH0 and PMC_RST_STATUS */
value |= 0x10; value = tegra_pmc_readl(PMC_CNTRL);
tegra_pmc_writel(value, 0); value |= PMC_CNTRL_MAIN_RST;
tegra_pmc_writel(value, PMC_CNTRL);
return NOTIFY_DONE; return NOTIFY_DONE;
} }
...@@ -722,13 +737,14 @@ static int tegra_powergate_of_get_clks(struct tegra_powergate *pg, ...@@ -722,13 +737,14 @@ static int tegra_powergate_of_get_clks(struct tegra_powergate *pg,
err: err:
while (i--) while (i--)
clk_put(pg->clks[i]); clk_put(pg->clks[i]);
kfree(pg->clks); kfree(pg->clks);
return err; return err;
} }
static int tegra_powergate_of_get_resets(struct tegra_powergate *pg, 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; struct reset_control *rst;
unsigned int i, count; unsigned int i, count;
...@@ -748,6 +764,16 @@ static int tegra_powergate_of_get_resets(struct tegra_powergate *pg, ...@@ -748,6 +764,16 @@ static int tegra_powergate_of_get_resets(struct tegra_powergate *pg,
err = PTR_ERR(pg->resets[i]); err = PTR_ERR(pg->resets[i]);
goto error; 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; pg->num_resets = count;
...@@ -757,6 +783,7 @@ static int tegra_powergate_of_get_resets(struct tegra_powergate *pg, ...@@ -757,6 +783,7 @@ static int tegra_powergate_of_get_resets(struct tegra_powergate *pg,
error: error:
while (i--) while (i--)
reset_control_put(pg->resets[i]); reset_control_put(pg->resets[i]);
kfree(pg->resets); kfree(pg->resets);
return err; return err;
...@@ -765,16 +792,19 @@ static int tegra_powergate_of_get_resets(struct tegra_powergate *pg, ...@@ -765,16 +792,19 @@ static int tegra_powergate_of_get_resets(struct tegra_powergate *pg,
static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
{ {
struct tegra_powergate *pg; struct tegra_powergate *pg;
int id, err;
bool off; bool off;
int id;
pg = kzalloc(sizeof(*pg), GFP_KERNEL); pg = kzalloc(sizeof(*pg), GFP_KERNEL);
if (!pg) if (!pg)
goto error; return;
id = tegra_powergate_lookup(pmc, np->name); 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; goto free_mem;
}
/* /*
* Clear the bit for this powergate so it cannot be managed * 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) ...@@ -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->genpd.power_on = tegra_genpd_power_on;
pg->pmc = pmc; 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; 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; 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); 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; goto remove_resets;
}
dev_dbg(pmc->dev, "added power domain %s\n", pg->genpd.name); dev_dbg(pmc->dev, "added power domain %s\n", pg->genpd.name);
return; return;
power_on_cleanup:
if (off)
WARN_ON(tegra_powergate_power_up(pg, true));
remove_resets: remove_resets:
while (pg->num_resets--) while (pg->num_resets--)
reset_control_put(pg->resets[pg->num_resets]); reset_control_put(pg->resets[pg->num_resets]);
kfree(pg->resets); kfree(pg->resets);
remove_clks: remove_clks:
while (pg->num_clks--) while (pg->num_clks--)
clk_put(pg->clks[pg->num_clks]); clk_put(pg->clks[pg->num_clks]);
kfree(pg->clks); kfree(pg->clks);
set_available: set_available:
...@@ -820,16 +883,20 @@ static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) ...@@ -820,16 +883,20 @@ static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
free_mem: free_mem:
kfree(pg); 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; 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) if (!np)
return; return;
...@@ -1205,6 +1272,14 @@ static int tegra_pmc_probe(struct platform_device *pdev) ...@@ -1205,6 +1272,14 @@ static int tegra_pmc_probe(struct platform_device *pdev)
struct resource *res; struct resource *res;
int err; 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); err = tegra_pmc_parse_dt(pmc, pdev->dev.of_node);
if (err < 0) if (err < 0)
return err; return err;
...@@ -1242,8 +1317,6 @@ static int tegra_pmc_probe(struct platform_device *pdev) ...@@ -1242,8 +1317,6 @@ static int tegra_pmc_probe(struct platform_device *pdev)
return err; return err;
} }
tegra_powergate_init(pmc);
mutex_lock(&pmc->powergates_lock); mutex_lock(&pmc->powergates_lock);
iounmap(pmc->base); iounmap(pmc->base);
pmc->base = base; pmc->base = base;
...@@ -1477,10 +1550,11 @@ static int __init tegra_pmc_early_init(void) ...@@ -1477,10 +1550,11 @@ static int __init tegra_pmc_early_init(void)
const struct of_device_id *match; const struct of_device_id *match;
struct device_node *np; struct device_node *np;
struct resource regs; struct resource regs;
unsigned int i;
bool invert; bool invert;
u32 value; u32 value;
mutex_init(&pmc->powergates_lock);
np = of_find_matching_node_and_match(NULL, tegra_pmc_match, &match); np = of_find_matching_node_and_match(NULL, tegra_pmc_match, &match);
if (!np) { if (!np) {
/* /*
...@@ -1515,39 +1589,40 @@ static int __init tegra_pmc_early_init(void) ...@@ -1515,39 +1589,40 @@ static int __init tegra_pmc_early_init(void)
*/ */
if (of_address_to_resource(np, 0, &regs) < 0) { if (of_address_to_resource(np, 0, &regs) < 0) {
pr_err("failed to get PMC registers\n"); pr_err("failed to get PMC registers\n");
of_node_put(np);
return -ENXIO; return -ENXIO;
} }
pmc->soc = match->data;
} }
pmc->base = ioremap_nocache(regs.start, resource_size(&regs)); pmc->base = ioremap_nocache(regs.start, resource_size(&regs));
if (!pmc->base) { if (!pmc->base) {
pr_err("failed to map PMC registers\n"); pr_err("failed to map PMC registers\n");
of_node_put(np);
return -ENXIO; return -ENXIO;
} }
/* Create a bit-map of the available and valid partitions */ if (np) {
for (i = 0; i < pmc->soc->num_powergates; i++) pmc->soc = match->data;
if (pmc->soc->powergates[i])
set_bit(i, pmc->powergates_available);
mutex_init(&pmc->powergates_lock); tegra_powergate_init(pmc, np);
/* /*
* Invert the interrupt polarity if a PMC device tree node exists and * Invert the interrupt polarity if a PMC device tree node
* contains the nvidia,invert-interrupt property. * exists and contains the nvidia,invert-interrupt property.
*/ */
invert = of_property_read_bool(np, "nvidia,invert-interrupt"); invert = of_property_read_bool(np, "nvidia,invert-interrupt");
value = tegra_pmc_readl(PMC_CNTRL); value = tegra_pmc_readl(PMC_CNTRL);
if (invert) if (invert)
value |= PMC_CNTRL_INTR_POLARITY; value |= PMC_CNTRL_INTR_POLARITY;
else else
value &= ~PMC_CNTRL_INTR_POLARITY; value &= ~PMC_CNTRL_INTR_POLARITY;
tegra_pmc_writel(value, PMC_CNTRL); tegra_pmc_writel(value, PMC_CNTRL);
of_node_put(np);
}
return 0; return 0;
} }
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
#ifndef __SOC_TEGRA_CPUIDLE_H__ #ifndef __SOC_TEGRA_CPUIDLE_H__
#define __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); void tegra_cpuidle_pcie_irqs_in_use(void);
#else #else
static inline void tegra_cpuidle_pcie_irqs_in_use(void) static inline void tegra_cpuidle_pcie_irqs_in_use(void)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment