soc/tegra: pmc: Parameterize driver

Parameterize some aspects of the driver in preparation for Tegra186 PMC
support. Initially the Tegra186 driver had been split off into an extra
driver, but it turns out the backwards-compatibility break isn't as bad
as originally assumed, so with a little parameterization the same code
can be used to keep supporting all SoC generations.

Signed-off-by: Thierry Reding <treding@nvidia.com>
This commit is contained in:
Thierry Reding 2017-08-30 12:32:58 +02:00
parent 75c15b90e4
commit 5be2255676

View File

@ -66,11 +66,10 @@
#define PMC_PWR_DET 0x48
#define PMC_SCRATCH0 0x50
#define PMC_SCRATCH0_MODE_RECOVERY BIT(31)
#define PMC_SCRATCH0_MODE_BOOTLOADER BIT(30)
#define PMC_SCRATCH0_MODE_RCM BIT(1)
#define PMC_SCRATCH0_MODE_MASK (PMC_SCRATCH0_MODE_RECOVERY | \
#define PMC_SCRATCH0_MODE_RECOVERY BIT(31)
#define PMC_SCRATCH0_MODE_BOOTLOADER BIT(30)
#define PMC_SCRATCH0_MODE_RCM BIT(1)
#define PMC_SCRATCH0_MODE_MASK (PMC_SCRATCH0_MODE_RECOVERY | \
PMC_SCRATCH0_MODE_BOOTLOADER | \
PMC_SCRATCH0_MODE_RCM)
@ -134,6 +133,14 @@ struct tegra_io_pad_soc {
unsigned int voltage;
};
struct tegra_pmc_regs {
unsigned int scratch0;
unsigned int dpd_req;
unsigned int dpd_status;
unsigned int dpd2_req;
unsigned int dpd2_status;
};
struct tegra_pmc_soc {
unsigned int num_powergates;
const char *const *powergates;
@ -145,6 +152,12 @@ struct tegra_pmc_soc {
const struct tegra_io_pad_soc *io_pads;
unsigned int num_io_pads;
const struct tegra_pmc_regs *regs;
void (*init)(struct tegra_pmc *pmc);
void (*setup_irq_polarity)(struct tegra_pmc *pmc,
struct device_node *np,
bool invert);
};
/**
@ -173,6 +186,7 @@ struct tegra_pmc_soc {
struct tegra_pmc {
struct device *dev;
void __iomem *base;
void __iomem *scratch;
struct clk *clk;
struct dentry *debugfs;
@ -645,7 +659,7 @@ static int tegra_pmc_restart_notify(struct notifier_block *this,
const char *cmd = data;
u32 value;
value = tegra_pmc_readl(PMC_SCRATCH0);
value = readl(pmc->scratch + pmc->soc->regs->scratch0);
value &= ~PMC_SCRATCH0_MODE_MASK;
if (cmd) {
@ -659,7 +673,7 @@ static int tegra_pmc_restart_notify(struct notifier_block *this,
value |= PMC_SCRATCH0_MODE_RCM;
}
tegra_pmc_writel(value, PMC_SCRATCH0);
writel(value, pmc->scratch + pmc->soc->regs->scratch0);
/* reset everything but PMC_SCRATCH0 and PMC_RST_STATUS */
value = tegra_pmc_readl(PMC_CNTRL);
@ -954,26 +968,28 @@ static int tegra_io_pad_prepare(enum tegra_io_pad id, unsigned long *request,
*mask = BIT(pad->dpd % 32);
if (pad->dpd < 32) {
*status = IO_DPD_STATUS;
*request = IO_DPD_REQ;
*status = pmc->soc->regs->dpd_status;
*request = pmc->soc->regs->dpd_req;
} else {
*status = IO_DPD2_STATUS;
*request = IO_DPD2_REQ;
*status = pmc->soc->regs->dpd2_status;
*request = pmc->soc->regs->dpd2_req;
}
rate = clk_get_rate(pmc->clk);
if (!rate) {
pr_err("failed to get clock rate\n");
return -ENODEV;
if (pmc->clk) {
rate = clk_get_rate(pmc->clk);
if (!rate) {
pr_err("failed to get clock rate\n");
return -ENODEV;
}
tegra_pmc_writel(DPD_SAMPLE_ENABLE, DPD_SAMPLE);
/* must be at least 200 ns, in APB (PCLK) clock cycles */
value = DIV_ROUND_UP(1000000000, rate);
value = DIV_ROUND_UP(200, value);
tegra_pmc_writel(value, SEL_DPD_TIM);
}
tegra_pmc_writel(DPD_SAMPLE_ENABLE, DPD_SAMPLE);
/* must be at least 200 ns, in APB (PCLK) clock cycles */
value = DIV_ROUND_UP(1000000000, rate);
value = DIV_ROUND_UP(200, value);
tegra_pmc_writel(value, SEL_DPD_TIM);
return 0;
}
@ -997,7 +1013,8 @@ static int tegra_io_pad_poll(unsigned long offset, u32 mask,
static void tegra_io_pad_unprepare(void)
{
tegra_pmc_writel(DPD_SAMPLE_DISABLE, DPD_SAMPLE);
if (pmc->clk)
tegra_pmc_writel(DPD_SAMPLE_DISABLE, DPD_SAMPLE);
}
/**
@ -1287,27 +1304,8 @@ static int tegra_pmc_parse_dt(struct tegra_pmc *pmc, struct device_node *np)
static void tegra_pmc_init(struct tegra_pmc *pmc)
{
u32 value;
/* Always enable CPU power request */
value = tegra_pmc_readl(PMC_CNTRL);
value |= PMC_CNTRL_CPU_PWRREQ_OE;
tegra_pmc_writel(value, PMC_CNTRL);
value = tegra_pmc_readl(PMC_CNTRL);
if (pmc->sysclkreq_high)
value &= ~PMC_CNTRL_SYSCLK_POLARITY;
else
value |= PMC_CNTRL_SYSCLK_POLARITY;
/* configure the output polarity while the request is tristated */
tegra_pmc_writel(value, PMC_CNTRL);
/* now enable the request */
value = tegra_pmc_readl(PMC_CNTRL);
value |= PMC_CNTRL_SYSCLK_OE;
tegra_pmc_writel(value, PMC_CNTRL);
if (pmc->soc->init)
pmc->soc->init(pmc);
}
static void tegra_pmc_init_tsense_reset(struct tegra_pmc *pmc)
@ -1410,11 +1408,18 @@ static int tegra_pmc_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
pmc->scratch = base;
pmc->clk = devm_clk_get(&pdev->dev, "pclk");
if (IS_ERR(pmc->clk)) {
err = PTR_ERR(pmc->clk);
dev_err(&pdev->dev, "failed to get pclk: %d\n", err);
return err;
if (err != -ENOENT) {
dev_err(&pdev->dev, "failed to get pclk: %d\n", err);
return err;
}
pmc->clk = NULL;
}
pmc->dev = &pdev->dev;
@ -1474,6 +1479,55 @@ static const char * const tegra20_powergates[] = {
[TEGRA_POWERGATE_MPE] = "mpe",
};
static const struct tegra_pmc_regs tegra20_pmc_regs = {
.scratch0 = 0x50,
.dpd_req = 0x1b8,
.dpd_status = 0x1bc,
.dpd2_req = 0x1c0,
.dpd2_status = 0x1c4,
};
static void tegra20_pmc_init(struct tegra_pmc *pmc)
{
u32 value;
/* Always enable CPU power request */
value = tegra_pmc_readl(PMC_CNTRL);
value |= PMC_CNTRL_CPU_PWRREQ_OE;
tegra_pmc_writel(value, PMC_CNTRL);
value = tegra_pmc_readl(PMC_CNTRL);
if (pmc->sysclkreq_high)
value &= ~PMC_CNTRL_SYSCLK_POLARITY;
else
value |= PMC_CNTRL_SYSCLK_POLARITY;
/* configure the output polarity while the request is tristated */
tegra_pmc_writel(value, PMC_CNTRL);
/* now enable the request */
value = tegra_pmc_readl(PMC_CNTRL);
value |= PMC_CNTRL_SYSCLK_OE;
tegra_pmc_writel(value, PMC_CNTRL);
}
static void tegra20_pmc_setup_irq_polarity(struct tegra_pmc *pmc,
struct device_node *np,
bool invert)
{
u32 value;
value = tegra_pmc_readl(PMC_CNTRL);
if (invert)
value |= PMC_CNTRL_INTR_POLARITY;
else
value &= ~PMC_CNTRL_INTR_POLARITY;
tegra_pmc_writel(value, PMC_CNTRL);
}
static const struct tegra_pmc_soc tegra20_pmc_soc = {
.num_powergates = ARRAY_SIZE(tegra20_powergates),
.powergates = tegra20_powergates,
@ -1481,6 +1535,11 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = {
.cpu_powergates = NULL,
.has_tsense_reset = false,
.has_gpu_clamps = false,
.num_io_pads = 0,
.io_pads = NULL,
.regs = &tegra20_pmc_regs,
.init = tegra20_pmc_init,
.setup_irq_polarity = tegra20_pmc_setup_irq_polarity,
};
static const char * const tegra30_powergates[] = {
@ -1514,6 +1573,11 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = {
.cpu_powergates = tegra30_cpu_powergates,
.has_tsense_reset = true,
.has_gpu_clamps = false,
.num_io_pads = 0,
.io_pads = NULL,
.regs = &tegra20_pmc_regs,
.init = tegra20_pmc_init,
.setup_irq_polarity = tegra20_pmc_setup_irq_polarity,
};
static const char * const tegra114_powergates[] = {
@ -1551,6 +1615,11 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = {
.cpu_powergates = tegra114_cpu_powergates,
.has_tsense_reset = true,
.has_gpu_clamps = false,
.num_io_pads = 0,
.io_pads = NULL,
.regs = &tegra20_pmc_regs,
.init = tegra20_pmc_init,
.setup_irq_polarity = tegra20_pmc_setup_irq_polarity,
};
static const char * const tegra124_powergates[] = {
@ -1628,6 +1697,9 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = {
.has_gpu_clamps = true,
.num_io_pads = ARRAY_SIZE(tegra124_io_pads),
.io_pads = tegra124_io_pads,
.regs = &tegra20_pmc_regs,
.init = tegra20_pmc_init,
.setup_irq_polarity = tegra20_pmc_setup_irq_polarity,
};
static const char * const tegra210_powergates[] = {
@ -1714,6 +1786,9 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = {
.has_gpu_clamps = true,
.num_io_pads = ARRAY_SIZE(tegra210_io_pads),
.io_pads = tegra210_io_pads,
.regs = &tegra20_pmc_regs,
.init = tegra20_pmc_init,
.setup_irq_polarity = tegra20_pmc_setup_irq_polarity,
};
static const struct of_device_id tegra_pmc_match[] = {
@ -1749,7 +1824,6 @@ static int __init tegra_pmc_early_init(void)
struct device_node *np;
struct resource regs;
bool invert;
u32 value;
mutex_init(&pmc->powergates_lock);
@ -1810,14 +1884,7 @@ static int __init tegra_pmc_early_init(void)
*/
invert = of_property_read_bool(np, "nvidia,invert-interrupt");
value = tegra_pmc_readl(PMC_CNTRL);
if (invert)
value |= PMC_CNTRL_INTR_POLARITY;
else
value &= ~PMC_CNTRL_INTR_POLARITY;
tegra_pmc_writel(value, PMC_CNTRL);
pmc->soc->setup_irq_polarity(pmc, np, invert);
of_node_put(np);
}