NVIDIA: SAUCE: soc/tegra: pmc: Add Tegra264 PMC driver

Existing PMC driver (pmc.c) is using a global struct tegra_pmc variable
rather than allocating the structure at probe time. This was necessary
for some legacy Tegra devices that required early configuration of some
PMC registers.

Add a new PMC driver (tegra264-pmc.c) for Tegra264 devices that leverages
the existing PMC driver but that avoids the need for a global tegra_pmc
variable.

http://nvbugs/4470933

Signed-off-by: Petlozu Pravareshwar <petlozup@nvidia.com>
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Acked-by: Noah Wager <noah.wager@canonical.com>
Acked-by: Jacob Martin <jacob.martin@canonical.com>
Signed-off-by: Noah Wager <noah.wager@canonical.com>
[nwager: Modified commit message]
Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Noah Wager <noah.wager@canonical.com>
This commit is contained in:
Petlozu Pravareshwar
2024-04-09 08:06:34 +00:00
committed by Noah Wager
parent 411945b55b
commit 409f2722e4
3 changed files with 502 additions and 0 deletions
+1
View File
@@ -5,6 +5,7 @@ obj-y += cbb/
obj-y += common.o
obj-$(CONFIG_SOC_TEGRA_FLOWCTRL) += flowctrl.o
obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o
obj-$(CONFIG_SOC_TEGRA_PMC) += tegra264-pmc.o
obj-$(CONFIG_SOC_TEGRA20_VOLTAGE_COUPLER) += regulators-tegra20.o
obj-$(CONFIG_SOC_TEGRA30_VOLTAGE_COUPLER) += regulators-tegra30.o
obj-$(CONFIG_ARCH_TEGRA_186_SOC) += ari-tegra186.o
+487
View File
@@ -0,0 +1,487 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* drivers/soc/tegra/tegra264-pmc.c
*
* Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
*
*/
#define pr_fmt(fmt) "tegra264-pmc: " fmt
#include <linux/device.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <soc/tegra/common.h>
#include <soc/tegra/fuse.h>
#include <soc/tegra/pmc.h>
#include <dt-bindings/pinctrl/pinctrl-tegra-io-pad.h>
static int tegra_pmc_reboot_notify(struct notifier_block *this,
unsigned long action, void *data)
{
struct tegra_pmc *pmc = container_of(this, struct tegra_pmc,
reboot_notifier);
if (action == SYS_RESTART)
tegra_pmc_program_reboot_reason(pmc, data);
return NOTIFY_DONE;
}
/**
* devm_tegra_pmc_get() - get Tegra PMC pointer
* @dev: device pointer of the Client device
*
* Return: ERR_PTR() on error or a valid pointer to Tegra PMC.
*/
static struct tegra_pmc *devm_tegra_pmc_get(struct device *dev)
{
struct platform_device *pdev;
struct tegra_pmc *pmc;
struct device_node *np;
np = of_parse_phandle(dev->of_node, "nvidia,pmc", 0);
if (!np)
return ERR_PTR(-ENOENT);
pdev = of_find_device_by_node(np);
of_node_put(np);
if (!pdev)
return ERR_PTR(-ENODEV);
pmc = platform_get_drvdata(pdev);
if (!pmc)
return ERR_PTR(-EPROBE_DEFER);
return pmc;
}
/**
* tegra264_io_pad_power_enable() - enable power to I/O pad
* @dev: device pointer of the Client device
* @id: Tegra I/O pad ID for which to enable power
*
* Returns: 0 on success or a negative error code on failure.
*/
int tegra264_io_pad_power_enable(struct device *dev, enum tegra_io_pad id)
{
struct tegra_pmc *pmc = devm_tegra_pmc_get(dev);
if (IS_ERR(pmc))
return PTR_ERR(pmc);
return tegra186_io_pad_power_enable(pmc, id);
}
EXPORT_SYMBOL(tegra264_io_pad_power_enable);
/**
* tegra264_io_pad_power_disable() - disable power to I/O pad
* @dev: device pointer of the Client device
* @id: Tegra I/O pad ID for which to enable power
*
* Returns: 0 on success or a negative error code on failure.
*/
int tegra264_io_pad_power_disable(struct device *dev, enum tegra_io_pad id)
{
struct tegra_pmc *pmc = devm_tegra_pmc_get(dev);
if (IS_ERR(pmc))
return PTR_ERR(pmc);
return tegra186_io_pad_power_disable(pmc, id);
}
EXPORT_SYMBOL(tegra264_io_pad_power_disable);
static int tegra_pmc_probe(struct platform_device *pdev)
{
struct tegra_pmc *pmc;
struct resource *res;
int err;
pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL);
if (!pmc)
return -ENOMEM;
pmc->soc = of_device_get_match_data(&pdev->dev);
pmc->dev = &pdev->dev;
/* take over the memory region from the early initialization */
pmc->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(pmc->base))
return PTR_ERR(pmc->base);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wake");
pmc->wake = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pmc->wake))
return PTR_ERR(pmc->wake);
/* "scratch" is an optional aperture */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scratch");
if (res) {
pmc->scratch = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pmc->scratch))
return PTR_ERR(pmc->scratch);
} else {
pmc->scratch = NULL;
}
/*
* PMC should be last resort for restarting since it soft-resets
* CPU without resetting everything else.
*/
if (pmc->scratch) {
pmc->reboot_notifier.notifier_call = tegra_pmc_reboot_notify;
err = devm_register_reboot_notifier(&pdev->dev,
&pmc->reboot_notifier);
if (err) {
dev_err(&pdev->dev,
"unable to register reboot notifier, %d\n",
err);
return err;
}
}
err = tegra_pmc_init(pmc);
if (err < 0) {
dev_err(&pdev->dev, "failed to initialize PMC: %d\n", err);
return err;
}
tegra_pmc_reset_sysfs_init(pmc);
err = tegra_pmc_pinctrl_init(pmc);
if (err)
goto cleanup_sysfs;
err = tegra_pmc_irq_init(pmc);
if (err < 0)
goto cleanup_sysfs;
/* Some wakes require specific filter configuration */
if (pmc->soc->set_wake_filters)
pmc->soc->set_wake_filters(pmc);
platform_set_drvdata(pdev, pmc);
return 0;
cleanup_sysfs:
tegra_pmc_reset_sysfs_remove(pmc);
return err;
}
static int __maybe_unused tegra_pmc_resume(struct device *dev)
{
struct tegra_pmc *pmc = dev_get_drvdata(dev);
tegra186_pmc_resume(pmc);
return 0;
}
static int __maybe_unused tegra_pmc_suspend(struct device *dev)
{
struct tegra_pmc *pmc = dev_get_drvdata(dev);
return tegra186_pmc_suspend(pmc);
}
static const struct dev_pm_ops tegra_pmc_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_pmc_suspend, tegra_pmc_resume)
};
static const char * const tegra264_reset_levels[] = {
"L0", "L1", "L2", "WARM"
};
#define TEGRA264_IO_PAD(_id, _dpd, _request, _status, _has_int_reg, _e_reg06, _e_reg18, _voltage, _e_33v_ctl, _name) \
((struct tegra_io_pad_soc) { \
.id = (_id), \
.dpd = (_dpd), \
.request = (_request), \
.status = (_status), \
.has_int_reg = (_has_int_reg), \
.e_reg06 = (_e_reg06), \
.e_reg18 = (_e_reg18), \
.voltage = (_voltage), \
.e_33v_ctl = (_e_33v_ctl), \
.name = (_name), \
})
#define TEGRA_IO_PIN_DESC(_id, _name) \
((struct pinctrl_pin_desc) { \
.number = (_id), \
.name = (_name), \
})
static const struct tegra_io_pad_soc tegra264_io_pads[] = {
TEGRA264_IO_PAD(TEGRA_IO_PAD_CSIA, 0, 0x41020, 0x41024, 0, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX, "csia"),
TEGRA264_IO_PAD(TEGRA_IO_PAD_CSIB, 1, 0x41020, 0x41024, 0, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX, "csib"),
TEGRA264_IO_PAD(TEGRA_IO_PAD_HDMI_DP0, 0, 0x41050, 0x41054, 0, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX, "hdmi-dp0"),
TEGRA264_IO_PAD(TEGRA_IO_PAD_CSIC, 2, 0x41020, 0x41024, 0, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX, "csic"),
TEGRA264_IO_PAD(TEGRA_IO_PAD_CSID, 3, 0x41020, 0x41024, 0, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX, "csid"),
TEGRA264_IO_PAD(TEGRA_IO_PAD_CSIE, 4, 0x41020, 0x41024, 0, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX, "csie"),
TEGRA264_IO_PAD(TEGRA_IO_PAD_CSIF, 5, 0x41020, 0x41024, 0, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX, "csif"),
TEGRA264_IO_PAD(TEGRA_IO_PAD_UFS, 4, 0x41040, 0x41044, 0, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX, "ufs0"),
TEGRA264_IO_PAD(TEGRA_IO_PAD_EDP, 0, 0x41028, 0x4102c, 0, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX, "edp"),
TEGRA264_IO_PAD(TEGRA_IO_PAD_SDMMC1, 0, 0x41090, 0x41094, 0, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX, "sdmmc1"),
TEGRA264_IO_PAD(TEGRA_IO_PAD_SDMMC1_HV, UINT_MAX, UINT_MAX, UINT_MAX, 1, 2, 1, 0, 0x41004, "sdmmc1-hv"),
TEGRA264_IO_PAD(TEGRA_IO_PAD_CSIG, 6, 0x41020, 0x41024, 0, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX, "csig"),
TEGRA264_IO_PAD(TEGRA_IO_PAD_CSIH, 7, 0x41020, 0x41024, 0, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX, "csih"),
};
static const struct pinctrl_pin_desc tegra264_pin_descs[] = {
TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_CSIA, "csia"),
TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_CSIB, "csib"),
TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_HDMI_DP0, "hdmi-dp0"),
TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_CSIC, "csic"),
TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_CSID, "csid"),
TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_CSIE, "csie"),
TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_CSIF, "csif"),
TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_UFS, "ufs0"),
TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_EDP, "edp"),
TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_SDMMC1, "sdmmc1"),
TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_SDMMC1_HV, "sdmmc1-hv"),
TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_CSIG, "csig"),
TEGRA_IO_PIN_DESC(TEGRA_IO_PAD_CSIH, "csih"),
};
static const struct tegra_pmc_regs tegra264_pmc_regs = {
.scratch0 = 0x684,
.rst_status = 0x4,
.rst_source_shift = 0x2,
.rst_source_mask = 0x1fc,
.rst_level_shift = 0x0,
.rst_level_mask = 0x3,
.aowake_cntrl = 0x0,
.aowake_mask_w = 0x200,
.aowake_status_w = 0x410,
.aowake_status_r = 0x610,
.aowake_tier2_routing = 0x660,
.aowake_sw_status_w = 0x624,
.aowake_sw_status = 0x628,
.aowake_latch_sw = 0x620,
.aowake_ctrl = 0x68c,
};
static const char * const tegra264_reset_sources[] = {
"SYS_RESET_N", /* 0 */
"CSDC_RTC_XTAL",
"VREFRO_POWER_BAD",
"SCPM_SOC_XTAL",
"SCPM_RTC_XTAL",
"FMON_32K",
"FMON_OSC",
"POD_RTC",
"POD_IO",
"POD_PLUS_IO_SPLL",
"POD_PLUS_SOC", /* 10 */
"VMON_PLUS_UV",
"VMON_PLUS_OV",
"FUSECRC_FAULT",
"OSC_FAULT",
"BPMP_BOOT_FAULT",
"SCPM_BPMP_CORE_CLK",
"SCPM_PSC_SE_CLK",
"VMON_SOC_MIN",
"VMON_SOC_MAX",
"VMON_MSS_MIN", /* 20 */
"VMON_MSS_MAX",
"POD_PLUS_IO_U4_TSENSE",
"SOC_THERM_FAULT",
"FSI_THERM_FAULT",
"PSC_TURTLE_MODE",
"SCPM_OESP_SE_CLK",
"SCPM_SB_SE_CLK",
"POD_CPU",
"POD_GPU",
"DCLS_GPU", /* 30 */
"POD_MSS",
"FSI_FMON",
"VMON_FSI_MIN",
"VMON_FSI_MAX",
"VMON_CPU_MIN",
"VMON_CPU_MAX",
"NVJTAG_SEL_MONITOR",
"BPMP_FMON",
"AO_WDT_POR",
"BPMP_WDT_POR", /* 40 */
"AO_TKE_WDT_POR",
"RCE0_WDT_POR",
"RCE1_WDT_POR",
"DCE_WDT_POR",
"PVA_0_WDT_POR",
"FSI_R5_WDT_POR",
"FSI_R52_0_WDT_POR",
"FSI_R52_1_WDT_POR",
"FSI_R52_2_WDT_POR",
"FSI_R52_3_WDT_POR", /* 50 */
"TOP_0_WDT_POR",
"TOP_1_WDT_POR",
"TOP_2_WDT_POR",
"APE_C0_WDT_POR",
"APE_C1_WDT_POR",
"GPU_TKE_WDT_POR",
"OESP_WDT_POR",
"SB_WDT_POR",
"PSC_WDT_POR",
"SW_MAIN", /* 60 */
"L0L1_RST_OUT_N",
"FSI_HSM",
"CSITE_SW",
"AO_WDT_DBG",
"BPMP_WDT_DBG",
"AO_TKE_WDT_DBG",
"RCE0_WDT_DBG",
"RCE1_WDT_DBG",
"DCE_WDT_DBG",
"PVA_0_WDT_DBG", /* 70 */
"FSI_R5_WDT_DBG",
"FSI_R52_0_WDT_DBG",
"FSI_R52_1_WDT_DBG",
"FSI_R52_2_WDT_DBG",
"FSI_R52_3_WDT_DBG",
"TOP_0_WDT_DBG",
"TOP_1_WDT_DBG",
"TOP_2_WDT_DBG",
"APE_C0_WDT_DBG",
"APE_C1_WDT_DBG", /* 80 */
"SB_WDT_DBG",
"OESP_WDT_DBG",
"PSC_WDT_DBG",
"TSC_0_WDT_DBG",
"TSC_1_WDT_DBG",
"L2_RST_OUT_N",
"SC7", /* 87 */
};
static const struct tegra_wake_event tegra264_wake_events[] = {
TEGRA_WAKE_IRQ("rtc", 65, 548),
};
static const struct tegra_pmc_soc tegra264_pmc_soc = {
.supports_core_domain = false,
.num_powergates = 0,
.powergates = NULL,
.num_cpu_powergates = 0,
.cpu_powergates = NULL,
.has_tsense_reset = false,
.has_gpu_clamps = false,
.needs_mbist_war = false,
.has_impl_33v_pwr = true,
.maybe_tz_only = false,
.num_io_pads = ARRAY_SIZE(tegra264_io_pads),
.io_pads = tegra264_io_pads,
.num_pin_descs = ARRAY_SIZE(tegra264_pin_descs),
.pin_descs = tegra264_pin_descs,
.regs = &tegra264_pmc_regs,
.init = NULL,
.setup_irq_polarity = tegra186_pmc_setup_irq_polarity,
.set_wake_filters = tegra186_pmc_set_wake_filters,
.irq_set_wake = tegra186_pmc_irq_set_wake,
.irq_set_type = tegra186_pmc_irq_set_type,
.reset_sources = tegra264_reset_sources,
.num_reset_sources = ARRAY_SIZE(tegra264_reset_sources),
.reset_levels = tegra264_reset_levels,
.num_reset_levels = ARRAY_SIZE(tegra264_reset_levels),
.num_wake_events = ARRAY_SIZE(tegra264_wake_events),
.wake_events = tegra264_wake_events,
.max_wake_events = 128,
.max_wake_vectors = 4,
.pmc_clks_data = NULL,
.num_pmc_clks = 0,
.has_blink_output = false,
.has_single_mmio_aperture = false,
};
static const struct of_device_id tegra_pmc_match[] = {
{ .compatible = "nvidia,tegra264-pmc", .data = &tegra264_pmc_soc },
{ }
};
static struct platform_driver tegra_pmc_driver = {
.driver = {
.name = "tegra264-pmc",
.suppress_bind_attrs = true,
.of_match_table = tegra_pmc_match,
.pm = &tegra_pmc_pm_ops,
},
.probe = tegra_pmc_probe,
};
builtin_platform_driver(tegra_pmc_driver);
/*
* Early initialization to allow access to registers in the very early boot
* process.
*/
static int __init tegra_pmc_early_init(void)
{
const struct of_device_id *match;
struct device_node *np = NULL;
struct resource regs;
struct tegra_pmc *pmc;
bool invert;
while (true) {
np = of_find_matching_node_and_match(np, tegra_pmc_match, &match);
if (np == NULL) {
/* No More Matches */
break;
}
pmc = kzalloc(sizeof(*pmc), GFP_KERNEL);
if (!pmc)
return -ENOMEM;
/*
* Extract information from the device tree if we've found a
* matching node.
*/
if (of_address_to_resource(np, 0, &regs) < 0) {
pr_err("failed to get PMC registers\n");
of_node_put(np);
return -ENXIO;
}
pmc->base = ioremap(regs.start, resource_size(&regs));
if (!pmc->base) {
pr_err("failed to map PMC registers\n");
of_node_put(np);
return -ENXIO;
}
if (of_device_is_available(np)) {
pmc->soc = match->data;
/*
* 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");
pmc->soc->setup_irq_polarity(pmc, np, invert);
of_node_put(np);
}
kfree(pmc);
}
return 0;
}
early_initcall(tegra_pmc_early_init);
+14
View File
@@ -378,6 +378,8 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk,
int tegra_io_pad_power_enable(enum tegra_io_pad id);
int tegra_io_pad_power_disable(enum tegra_io_pad id);
int tegra264_io_pad_power_enable(struct device *dev, enum tegra_io_pad id);
int tegra264_io_pad_power_disable(struct device *dev, enum tegra_io_pad id);
/* deprecated, use tegra_io_pad_power_{enable,disable}() instead */
int tegra_io_rail_power_on(unsigned int id);
@@ -441,6 +443,18 @@ static inline int tegra_io_pad_power_disable(enum tegra_io_pad id)
return -ENOSYS;
}
static inline int tegra264_io_pad_power_enable(struct device *dev,
enum tegra_io_pad id)
{
return -ENOSYS;
}
static inline int tegra264_io_pad_power_disable(struct device *dev,
enum tegra_io_pad id)
{
return -ENOSYS;
}
static inline int tegra_io_pad_get_voltage(enum tegra_io_pad id)
{
return -ENOSYS;