From patchwork Sat Sep 6 08:54:45 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mikko Perttunen X-Patchwork-Id: 4857121 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 4C3E2C0338 for ; Sat, 6 Sep 2014 08:59:08 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 0890020155 for ; Sat, 6 Sep 2014 08:59:07 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id AA1BB20142 for ; Sat, 6 Sep 2014 08:59:05 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1XQBmk-0005Ny-Vp; Sat, 06 Sep 2014 08:55:54 +0000 Received: from mx1.kapsi.fi ([2001:1bc8:1004::1:25] helo=mail.kapsi.fi) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1XQBmc-0005IY-1a for linux-arm-kernel@lists.infradead.org; Sat, 06 Sep 2014 08:55:47 +0000 Received: from [2001:708:30:12d0:beee:7bff:fe5b:f272] (helo=katsura.kyla.fi) by mail.kapsi.fi with esmtpsa (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.72) (envelope-from ) id 1XQBm5-0006Uz-RR; Sat, 06 Sep 2014 11:55:13 +0300 From: Mikko Perttunen To: swarren@wwwdotorg.org, thierry.reding@gmail.com Subject: [PATCH v3 5/5] ARM: tegra: Add thermal reset (thermtrip) support to PMC Date: Sat, 6 Sep 2014 11:54:45 +0300 Message-Id: <1409993685-21911-6-git-send-email-cyndis@kapsi.fi> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1409993685-21911-1-git-send-email-cyndis@kapsi.fi> References: <1409993685-21911-1-git-send-email-cyndis@kapsi.fi> X-SA-Exim-Connect-IP: 2001:708:30:12d0:beee:7bff:fe5b:f272 X-SA-Exim-Mail-From: cyndis@kapsi.fi X-SA-Exim-Scanned: No (on mail.kapsi.fi); SAEximRunCond expanded to false X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140906_015546_446544_41B3E72F X-CRM114-Status: GOOD ( 17.21 ) X-Spam-Score: -1.7 (-) Cc: linux-tegra@vger.kernel.org, wni@nvidia.com, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Mikko Perttunen X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-3.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Mikko Perttunen This adds a device tree controlled option to enable PMC-based thermal reset in overheating situations. Thermtrip is supported on Tegra30, Tegra114 and Tegra124. The thermal reset only works when the thermal sensors are calibrated, so a soctherm driver is also required. The thermtrip event is triggered by the soctherm block, and all soctherm sensors default to showing a temperature of zero Celsius before they are initialized. Because of this, it is safe to initialize thermtrip and soctherm in any order. Signed-off-by: Mikko Perttunen Reviewed-by: Wei Ni Tested-by: Wei Ni --- v3 - updated to new binding - updated commit message drivers/soc/tegra/pmc.c | 379 +++++++++++++++++++++++++++++++----------------- 1 file changed, 247 insertions(+), 132 deletions(-) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index a2c0ceb..de514fe 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -83,11 +83,28 @@ #define GPU_RG_CNTRL 0x2d4 +#define PMC_SENSOR_CTRL 0x1b0 +#define PMC_SENSOR_CTRL_SCRATCH_WRITE (1 << 2) +#define PMC_SENSOR_CTRL_ENABLE_RST (1 << 1) + +#define PMC_SCRATCH54 0x258 +#define PMC_SCRATCH54_DATA_SHIFT 8 +#define PMC_SCRATCH54_ADDR_SHIFT 0 + +#define PMC_SCRATCH55 0x25c +#define PMC_SCRATCH55_RESET_TEGRA (1 << 31) +#define PMC_SCRATCH55_CNTRL_ID_SHIFT 27 +#define PMC_SCRATCH55_PINMUX_SHIFT 24 +#define PMC_SCRATCH55_16BITOP (1 << 15) +#define PMC_SCRATCH55_CHECKSUM_SHIFT 16 +#define PMC_SCRATCH55_I2CSLV1_SHIFT 0 + struct tegra_pmc_soc { unsigned int num_powergates; const char *const *powergates; unsigned int num_cpu_powergates; const u8 *cpu_powergates; + bool has_tsense_reset; }; /** @@ -606,6 +623,234 @@ void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode) } #endif +static const char * const tegra20_powergates[] = { + [TEGRA_POWERGATE_CPU] = "cpu", + [TEGRA_POWERGATE_3D] = "3d", + [TEGRA_POWERGATE_VENC] = "venc", + [TEGRA_POWERGATE_VDEC] = "vdec", + [TEGRA_POWERGATE_PCIE] = "pcie", + [TEGRA_POWERGATE_L2] = "l2", + [TEGRA_POWERGATE_MPE] = "mpe", +}; + +static const struct tegra_pmc_soc tegra20_pmc_soc = { + .num_powergates = ARRAY_SIZE(tegra20_powergates), + .powergates = tegra20_powergates, + .num_cpu_powergates = 0, + .cpu_powergates = NULL, +}; + +static const char * const tegra30_powergates[] = { + [TEGRA_POWERGATE_CPU] = "cpu0", + [TEGRA_POWERGATE_3D] = "3d0", + [TEGRA_POWERGATE_VENC] = "venc", + [TEGRA_POWERGATE_VDEC] = "vdec", + [TEGRA_POWERGATE_PCIE] = "pcie", + [TEGRA_POWERGATE_L2] = "l2", + [TEGRA_POWERGATE_MPE] = "mpe", + [TEGRA_POWERGATE_HEG] = "heg", + [TEGRA_POWERGATE_SATA] = "sata", + [TEGRA_POWERGATE_CPU1] = "cpu1", + [TEGRA_POWERGATE_CPU2] = "cpu2", + [TEGRA_POWERGATE_CPU3] = "cpu3", + [TEGRA_POWERGATE_CELP] = "celp", + [TEGRA_POWERGATE_3D1] = "3d1", +}; + +static const u8 tegra30_cpu_powergates[] = { + TEGRA_POWERGATE_CPU, + TEGRA_POWERGATE_CPU1, + TEGRA_POWERGATE_CPU2, + TEGRA_POWERGATE_CPU3, +}; + +static const struct tegra_pmc_soc tegra30_pmc_soc = { + .num_powergates = ARRAY_SIZE(tegra30_powergates), + .powergates = tegra30_powergates, + .num_cpu_powergates = ARRAY_SIZE(tegra30_cpu_powergates), + .cpu_powergates = tegra30_cpu_powergates, + .has_tsense_reset = true, +}; + +static const char * const tegra114_powergates[] = { + [TEGRA_POWERGATE_CPU] = "crail", + [TEGRA_POWERGATE_3D] = "3d", + [TEGRA_POWERGATE_VENC] = "venc", + [TEGRA_POWERGATE_VDEC] = "vdec", + [TEGRA_POWERGATE_MPE] = "mpe", + [TEGRA_POWERGATE_HEG] = "heg", + [TEGRA_POWERGATE_CPU1] = "cpu1", + [TEGRA_POWERGATE_CPU2] = "cpu2", + [TEGRA_POWERGATE_CPU3] = "cpu3", + [TEGRA_POWERGATE_CELP] = "celp", + [TEGRA_POWERGATE_CPU0] = "cpu0", + [TEGRA_POWERGATE_C0NC] = "c0nc", + [TEGRA_POWERGATE_C1NC] = "c1nc", + [TEGRA_POWERGATE_DIS] = "dis", + [TEGRA_POWERGATE_DISB] = "disb", + [TEGRA_POWERGATE_XUSBA] = "xusba", + [TEGRA_POWERGATE_XUSBB] = "xusbb", + [TEGRA_POWERGATE_XUSBC] = "xusbc", +}; + +static const u8 tegra114_cpu_powergates[] = { + TEGRA_POWERGATE_CPU0, + TEGRA_POWERGATE_CPU1, + TEGRA_POWERGATE_CPU2, + TEGRA_POWERGATE_CPU3, +}; + +static const struct tegra_pmc_soc tegra114_pmc_soc = { + .num_powergates = ARRAY_SIZE(tegra114_powergates), + .powergates = tegra114_powergates, + .num_cpu_powergates = ARRAY_SIZE(tegra114_cpu_powergates), + .cpu_powergates = tegra114_cpu_powergates, + .has_tsense_reset = true, +}; + +static const char * const tegra124_powergates[] = { + [TEGRA_POWERGATE_CPU] = "crail", + [TEGRA_POWERGATE_3D] = "3d", + [TEGRA_POWERGATE_VENC] = "venc", + [TEGRA_POWERGATE_PCIE] = "pcie", + [TEGRA_POWERGATE_VDEC] = "vdec", + [TEGRA_POWERGATE_L2] = "l2", + [TEGRA_POWERGATE_MPE] = "mpe", + [TEGRA_POWERGATE_HEG] = "heg", + [TEGRA_POWERGATE_SATA] = "sata", + [TEGRA_POWERGATE_CPU1] = "cpu1", + [TEGRA_POWERGATE_CPU2] = "cpu2", + [TEGRA_POWERGATE_CPU3] = "cpu3", + [TEGRA_POWERGATE_CELP] = "celp", + [TEGRA_POWERGATE_CPU0] = "cpu0", + [TEGRA_POWERGATE_C0NC] = "c0nc", + [TEGRA_POWERGATE_C1NC] = "c1nc", + [TEGRA_POWERGATE_SOR] = "sor", + [TEGRA_POWERGATE_DIS] = "dis", + [TEGRA_POWERGATE_DISB] = "disb", + [TEGRA_POWERGATE_XUSBA] = "xusba", + [TEGRA_POWERGATE_XUSBB] = "xusbb", + [TEGRA_POWERGATE_XUSBC] = "xusbc", + [TEGRA_POWERGATE_VIC] = "vic", + [TEGRA_POWERGATE_IRAM] = "iram", +}; + +static const u8 tegra124_cpu_powergates[] = { + TEGRA_POWERGATE_CPU0, + TEGRA_POWERGATE_CPU1, + TEGRA_POWERGATE_CPU2, + TEGRA_POWERGATE_CPU3, +}; + +static const struct tegra_pmc_soc tegra124_pmc_soc = { + .num_powergates = ARRAY_SIZE(tegra124_powergates), + .powergates = tegra124_powergates, + .num_cpu_powergates = ARRAY_SIZE(tegra124_cpu_powergates), + .cpu_powergates = tegra124_cpu_powergates, + .has_tsense_reset = true, +}; + +static const struct of_device_id tegra_pmc_match[] = { + { .compatible = "nvidia,tegra124-pmc", .data = &tegra124_pmc_soc }, + { .compatible = "nvidia,tegra114-pmc", .data = &tegra114_pmc_soc }, + { .compatible = "nvidia,tegra30-pmc", .data = &tegra30_pmc_soc }, + { .compatible = "nvidia,tegra20-pmc", .data = &tegra20_pmc_soc }, + { } +}; + +void tegra_pmc_init_tsense_reset(struct device *dev) +{ + u32 pmu_i2c_addr, i2c_ctrl_id, reg_addr, reg_data, pinmux; + u32 value, checksum; + struct device_node *np = dev->of_node; + struct device_node *i2c_np, *tt_np; + const struct of_device_id *match = of_match_node(tegra_pmc_match, np); + const struct tegra_pmc_soc *data = match->data; + + if (!data->has_tsense_reset) + return; + + tt_np = of_find_node_by_name(np, "i2c-thermtrip"); + if (!tt_np) { + dev_warn(dev, "no i2c-thermtrip node found, disabling emergency thermal reset\n"); + return; + } + + i2c_np = of_parse_phandle(tt_np, "nvidia,i2c-bus", 0); + if (!i2c_np) { + dev_err(dev, "I2C bus reference missing, disabling emergency thermal reset\n"); + goto put_tt; + } + + if (of_property_read_u32(i2c_np, "nvidia,controller-id", &i2c_ctrl_id)) { + dev_err(dev, "I2C bus controller id missing, disabling emergency thermal reset\n"); + goto put_i2c; + } + + of_node_put(i2c_np); + + if (of_property_read_u32(tt_np, "nvidia,bus-addr", &pmu_i2c_addr)) { + dev_err(dev, "nvidia,bus-addr missing, disabling emergency thermal reset\n"); + goto put_tt; + } + + if (of_property_read_u32(tt_np, "nvidia,reg-addr", ®_addr)) { + dev_err(dev, "nvidia,reg-addr missing, disabling emergency thermal reset\n"); + goto put_tt; + } + + if (of_property_read_u32(tt_np, "nvidia,reg-data", ®_data)) { + dev_err(dev, "nvidia,reg-data missing, disabling emergency thermal reset\n"); + goto put_tt; + } + + if (of_property_read_u32(tt_np, "nvidia,pinmux-id", &pinmux)) + pinmux = 0; + + of_node_put(tt_np); + + value = tegra_pmc_readl(PMC_SENSOR_CTRL); + value |= PMC_SENSOR_CTRL_SCRATCH_WRITE; + tegra_pmc_writel(value, PMC_SENSOR_CTRL); + + value = (reg_data << PMC_SCRATCH54_DATA_SHIFT) | + (reg_addr << PMC_SCRATCH54_ADDR_SHIFT); + tegra_pmc_writel(value, PMC_SCRATCH54); + + value = 0; + value |= PMC_SCRATCH55_RESET_TEGRA; + value |= i2c_ctrl_id << PMC_SCRATCH55_CNTRL_ID_SHIFT; + value |= pinmux << PMC_SCRATCH55_PINMUX_SHIFT; + value |= pmu_i2c_addr << PMC_SCRATCH55_I2CSLV1_SHIFT; + + /* Calculate checksum of SCRATCH54, SCRATCH55 fields. + * Bits 23:16 will contain the checksum and are currently zero, + * so they are not added. + */ + checksum = reg_addr + reg_data + (value & 0xff) + ((value >> 8) & 0xff) + + ((value >> 24) & 0xff); + checksum &= 0xff; + checksum = 0x100 - checksum; + + value |= checksum << PMC_SCRATCH55_CHECKSUM_SHIFT; + + tegra_pmc_writel(value, PMC_SCRATCH55); + + value = tegra_pmc_readl(PMC_SENSOR_CTRL); + value |= PMC_SENSOR_CTRL_ENABLE_RST; + tegra_pmc_writel(value, PMC_SENSOR_CTRL); + + dev_info(dev, "PMC emergency thermal reset enabled\n"); + + return; + +put_i2c: + of_node_put(i2c_np); + +put_tt: + of_node_put(tt_np); +} + static int tegra_pmc_parse_dt(struct tegra_pmc *pmc, struct device_node *np) { u32 value, values[2]; @@ -730,6 +975,8 @@ static int tegra_pmc_probe(struct platform_device *pdev) tegra_pmc_init(pmc); + tegra_pmc_init_tsense_reset(&pdev->dev); + if (IS_ENABLED(CONFIG_DEBUG_FS)) { err = tegra_powergate_debugfs_init(); if (err < 0) @@ -757,138 +1004,6 @@ static int tegra_pmc_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(tegra_pmc_pm_ops, tegra_pmc_suspend, tegra_pmc_resume); -static const char * const tegra20_powergates[] = { - [TEGRA_POWERGATE_CPU] = "cpu", - [TEGRA_POWERGATE_3D] = "3d", - [TEGRA_POWERGATE_VENC] = "venc", - [TEGRA_POWERGATE_VDEC] = "vdec", - [TEGRA_POWERGATE_PCIE] = "pcie", - [TEGRA_POWERGATE_L2] = "l2", - [TEGRA_POWERGATE_MPE] = "mpe", -}; - -static const struct tegra_pmc_soc tegra20_pmc_soc = { - .num_powergates = ARRAY_SIZE(tegra20_powergates), - .powergates = tegra20_powergates, - .num_cpu_powergates = 0, - .cpu_powergates = NULL, -}; - -static const char * const tegra30_powergates[] = { - [TEGRA_POWERGATE_CPU] = "cpu0", - [TEGRA_POWERGATE_3D] = "3d0", - [TEGRA_POWERGATE_VENC] = "venc", - [TEGRA_POWERGATE_VDEC] = "vdec", - [TEGRA_POWERGATE_PCIE] = "pcie", - [TEGRA_POWERGATE_L2] = "l2", - [TEGRA_POWERGATE_MPE] = "mpe", - [TEGRA_POWERGATE_HEG] = "heg", - [TEGRA_POWERGATE_SATA] = "sata", - [TEGRA_POWERGATE_CPU1] = "cpu1", - [TEGRA_POWERGATE_CPU2] = "cpu2", - [TEGRA_POWERGATE_CPU3] = "cpu3", - [TEGRA_POWERGATE_CELP] = "celp", - [TEGRA_POWERGATE_3D1] = "3d1", -}; - -static const u8 tegra30_cpu_powergates[] = { - TEGRA_POWERGATE_CPU, - TEGRA_POWERGATE_CPU1, - TEGRA_POWERGATE_CPU2, - TEGRA_POWERGATE_CPU3, -}; - -static const struct tegra_pmc_soc tegra30_pmc_soc = { - .num_powergates = ARRAY_SIZE(tegra30_powergates), - .powergates = tegra30_powergates, - .num_cpu_powergates = ARRAY_SIZE(tegra30_cpu_powergates), - .cpu_powergates = tegra30_cpu_powergates, -}; - -static const char * const tegra114_powergates[] = { - [TEGRA_POWERGATE_CPU] = "crail", - [TEGRA_POWERGATE_3D] = "3d", - [TEGRA_POWERGATE_VENC] = "venc", - [TEGRA_POWERGATE_VDEC] = "vdec", - [TEGRA_POWERGATE_MPE] = "mpe", - [TEGRA_POWERGATE_HEG] = "heg", - [TEGRA_POWERGATE_CPU1] = "cpu1", - [TEGRA_POWERGATE_CPU2] = "cpu2", - [TEGRA_POWERGATE_CPU3] = "cpu3", - [TEGRA_POWERGATE_CELP] = "celp", - [TEGRA_POWERGATE_CPU0] = "cpu0", - [TEGRA_POWERGATE_C0NC] = "c0nc", - [TEGRA_POWERGATE_C1NC] = "c1nc", - [TEGRA_POWERGATE_DIS] = "dis", - [TEGRA_POWERGATE_DISB] = "disb", - [TEGRA_POWERGATE_XUSBA] = "xusba", - [TEGRA_POWERGATE_XUSBB] = "xusbb", - [TEGRA_POWERGATE_XUSBC] = "xusbc", -}; - -static const u8 tegra114_cpu_powergates[] = { - TEGRA_POWERGATE_CPU0, - TEGRA_POWERGATE_CPU1, - TEGRA_POWERGATE_CPU2, - TEGRA_POWERGATE_CPU3, -}; - -static const struct tegra_pmc_soc tegra114_pmc_soc = { - .num_powergates = ARRAY_SIZE(tegra114_powergates), - .powergates = tegra114_powergates, - .num_cpu_powergates = ARRAY_SIZE(tegra114_cpu_powergates), - .cpu_powergates = tegra114_cpu_powergates, -}; - -static const char * const tegra124_powergates[] = { - [TEGRA_POWERGATE_CPU] = "crail", - [TEGRA_POWERGATE_3D] = "3d", - [TEGRA_POWERGATE_VENC] = "venc", - [TEGRA_POWERGATE_PCIE] = "pcie", - [TEGRA_POWERGATE_VDEC] = "vdec", - [TEGRA_POWERGATE_L2] = "l2", - [TEGRA_POWERGATE_MPE] = "mpe", - [TEGRA_POWERGATE_HEG] = "heg", - [TEGRA_POWERGATE_SATA] = "sata", - [TEGRA_POWERGATE_CPU1] = "cpu1", - [TEGRA_POWERGATE_CPU2] = "cpu2", - [TEGRA_POWERGATE_CPU3] = "cpu3", - [TEGRA_POWERGATE_CELP] = "celp", - [TEGRA_POWERGATE_CPU0] = "cpu0", - [TEGRA_POWERGATE_C0NC] = "c0nc", - [TEGRA_POWERGATE_C1NC] = "c1nc", - [TEGRA_POWERGATE_SOR] = "sor", - [TEGRA_POWERGATE_DIS] = "dis", - [TEGRA_POWERGATE_DISB] = "disb", - [TEGRA_POWERGATE_XUSBA] = "xusba", - [TEGRA_POWERGATE_XUSBB] = "xusbb", - [TEGRA_POWERGATE_XUSBC] = "xusbc", - [TEGRA_POWERGATE_VIC] = "vic", - [TEGRA_POWERGATE_IRAM] = "iram", -}; - -static const u8 tegra124_cpu_powergates[] = { - TEGRA_POWERGATE_CPU0, - TEGRA_POWERGATE_CPU1, - TEGRA_POWERGATE_CPU2, - TEGRA_POWERGATE_CPU3, -}; - -static const struct tegra_pmc_soc tegra124_pmc_soc = { - .num_powergates = ARRAY_SIZE(tegra124_powergates), - .powergates = tegra124_powergates, - .num_cpu_powergates = ARRAY_SIZE(tegra124_cpu_powergates), - .cpu_powergates = tegra124_cpu_powergates, -}; - -static const struct of_device_id tegra_pmc_match[] = { - { .compatible = "nvidia,tegra124-pmc", .data = &tegra124_pmc_soc }, - { .compatible = "nvidia,tegra114-pmc", .data = &tegra114_pmc_soc }, - { .compatible = "nvidia,tegra30-pmc", .data = &tegra30_pmc_soc }, - { .compatible = "nvidia,tegra20-pmc", .data = &tegra20_pmc_soc }, - { } -}; - static struct platform_driver tegra_pmc_driver = { .driver = { .name = "tegra-pmc",