From patchwork Tue Oct 3 11:16:34 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mateusz Majewski X-Patchwork-Id: 13407483 X-Patchwork-Delegate: daniel.lezcano@linaro.org Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 21104E7544C for ; Tue, 3 Oct 2023 11:17:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S240495AbjJCLRj (ORCPT ); Tue, 3 Oct 2023 07:17:39 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58550 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S240498AbjJCLRd (ORCPT ); Tue, 3 Oct 2023 07:17:33 -0400 Received: from mailout1.w1.samsung.com (mailout1.w1.samsung.com [210.118.77.11]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 50800BD for ; Tue, 3 Oct 2023 04:17:17 -0700 (PDT) Received: from eucas1p2.samsung.com (unknown [182.198.249.207]) by mailout1.w1.samsung.com (KnoxPortal) with ESMTP id 20231003111715euoutp01a87c1ec2c26d32a692786922d2ceaee6~KlNEzJFrR0934109341euoutp01k for ; Tue, 3 Oct 2023 11:17:15 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.w1.samsung.com 20231003111715euoutp01a87c1ec2c26d32a692786922d2ceaee6~KlNEzJFrR0934109341euoutp01k DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1696331835; bh=6pQq2j4oWPpU1K39ZQsmq/adTTse3sbmpHJoM81es1Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Eraxu/fb5ZCo7H93DbwzRK1XUr6AQd5WDulayTDlMJuqYaYkLjqQSne4eLt0dgLWe d778B/UCUZl4TEl9mejlZpRVUStjnO7wdGV41F61DsXopgGXmgKF77OjafjXWI+SZh QADKQ3p5yQb0jUvE0z5NoHLhrQ8nnyNcC77uzRbw= Received: from eusmges1new.samsung.com (unknown [203.254.199.242]) by eucas1p2.samsung.com (KnoxPortal) with ESMTP id 20231003111715eucas1p242320a38764f9c2111fc0e73770a1ead~KlNEWfVLt2870828708eucas1p2D; Tue, 3 Oct 2023 11:17:15 +0000 (GMT) Received: from eucas1p2.samsung.com ( [182.198.249.207]) by eusmges1new.samsung.com (EUCPMTA) with SMTP id B3.08.42423.B38FB156; Tue, 3 Oct 2023 12:17:15 +0100 (BST) Received: from eusmtrp1.samsung.com (unknown [182.198.249.138]) by eucas1p1.samsung.com (KnoxPortal) with ESMTPA id 20231003111714eucas1p19c2351845d47b4ed1da8bc1b866e3353~KlND5H1Q02462224622eucas1p1a; Tue, 3 Oct 2023 11:17:14 +0000 (GMT) Received: from eusmgms1.samsung.com (unknown [182.198.249.179]) by eusmtrp1.samsung.com (KnoxPortal) with ESMTP id 20231003111714eusmtrp13d62977ea24e73fb37e09beb164436cc~KlND4NY732239722397eusmtrp1M; Tue, 3 Oct 2023 11:17:14 +0000 (GMT) X-AuditID: cbfec7f2-a51ff7000002a5b7-d9-651bf83b66af Received: from eusmtip2.samsung.com ( [203.254.199.222]) by eusmgms1.samsung.com (EUCPMTA) with SMTP id C1.E1.10549.A38FB156; Tue, 3 Oct 2023 12:17:14 +0100 (BST) Received: from AMDC4515.eu.corp.samsungelectronics.net (unknown [106.120.51.28]) by eusmtip2.samsung.com (KnoxPortal) with ESMTPA id 20231003111714eusmtip2b5a0ce422071b88639a4df98a7cd2e3d~KlNDFMiJz3116931169eusmtip2e; Tue, 3 Oct 2023 11:17:13 +0000 (GMT) From: Mateusz Majewski To: linux-pm@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Cc: Mateusz Majewski , Bartlomiej Zolnierkiewicz , Krzysztof Kozlowski , "Rafael J. Wysocki" , Daniel Lezcano , Amit Kucheria , Zhang Rui , Alim Akhtar , Liam Girdwood , Mark Brown , Marek Szyprowski Subject: [PATCH v3 8/8] thermal: exynos: use set_trips Date: Tue, 3 Oct 2023 13:16:34 +0200 Message-ID: <20231003111638.241542-9-m.majewski2@samsung.com> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231003111638.241542-1-m.majewski2@samsung.com> MIME-Version: 1.0 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrPKsWRmVeSWpSXmKPExsWy7djP87rWP6RTDZ4cE7J4MG8bm8Xh+RUW Ux8+YbP4vuU6k8W8z7IWe19vZbf4dqWDyWLT42usFpd3zWGz+Nx7hNFixvl9TBYTj01mtlh7 5C67xdwvU5ktnjzsY3Pg99g56y67x+I9L5k8Nq3qZPO4c20Pm8fmJfUefVtWMXp83iQXwB7F ZZOSmpNZllqkb5fAlXG0+Q57QeMaxoprpw6zNTDO6GXsYuTkkBAwkTh/pZmpi5GLQ0hgBaNE x8wPzBDOF0aJp/2/WUCqhAQ+M0qsXJYF09Hw+TY7RNFyRon5a3dCdbQySexuXg3WwSZgIPHg zTJ2EFtEoJVRYmaTOkgRs8AlZom7N9eCFQkLmEncm/aeGcRmEVCVeHr5BROIzStgK7HxSxMb xDp5iT2LvoPFOQXsJFb+XA5VIyhxcuYTsDnMQDXNW2czQ9Q3c0rsbwyGsF0k5my4yAJhC0u8 Or6FHcKWkTg9uQcqni8xY/N7IJsDyK6QuHvQC8K0lvh4hhnEZBbQlFi/Sx+i2FFiwXuQ30Eq +CRuvBWE2M8nMWnbdGaIMK9ER5sQRLWqxPE9k6DOkpZ40nKbCcL2kLh2u491AqPiLCSfzELy ySyEvQsYmVcxiqeWFuempxYb5qWW6xUn5haX5qXrJefnbmIEprHT/45/2sE499VHvUOMTByM hxglOJiVRHjDjkqlCvGmJFZWpRblxxeV5qQWH2KU5mBREufVtj2ZLCSQnliSmp2aWpBaBJNl 4uCUamBK8xW3F3t9drK+3EaOv8nei7Vl+XojP3Jc0PvNducWd8fq9IvKG6v2/5w5PSB5s99k g0mv1jtE7JWz9b9+Oa55q9Ljtxd8/nF6coYcaXPVDzPRv3y7e1vKc2nlG7sjjrf8PMDCVXp6 /+QA7/XLHyqLfn5y2ao7V7DJ63XMd9bbBz/nut20Dnoh41qUy/ix/+Gf7DMsqu/8WZzuzFCa vCP5ysVjtU6rZv0qW23H8SLOSsrnrzGD5RbBRml7Lr/W/7rL4squ/Zy3fInH8fDJwRFtHmX6 O7JVmE1Phe7Kvuz2Zf6E+APNOsa87XVJdQzW4cITlTb38Z24cPb7rvvcB9U33XZRl7jvZrRw dUbzGRYlluKMREMt5qLiRAB0mJdr0gMAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrEIsWRmVeSWpSXmKPExsVy+t/xe7pWP6RTDT5sMrR4MG8bm8Xh+RUW Ux8+YbP4vuU6k8W8z7IWe19vZbf4dqWDyWLT42usFpd3zWGz+Nx7hNFixvl9TBYTj01mtlh7 5C67xdwvU5ktnjzsY3Pg99g56y67x+I9L5k8Nq3qZPO4c20Pm8fmJfUefVtWMXp83iQXwB6l Z1OUX1qSqpCRX1xiqxRtaGGkZ2hpoWdkYqlnaGwea2VkqqRvZ5OSmpNZllqkb5egl3G0+Q57 QeMaxoprpw6zNTDO6GXsYuTkkBAwkWj4fJu9i5GLQ0hgKaPE8T1f2CAS0hKHv0xhh7CFJf5c 62KDKGpmkmjrP8UEkmATMJB48GYZWLeIQCejRNfmc2AJZoFbzBL3X2uB2MICZhL3pr1nBrFZ BFQlnl5+AVbDK2ArsfFLE9Q2eYk9i76DxTkF7CRW/lwOZgsB1dxavo8Fol5Q4uTMJywQ8+Ul mrfOZp7AKDALSWoWktQCRqZVjCKppcW56bnFhnrFibnFpXnpesn5uZsYgZG37djPzTsY5736 qHeIkYmD8RCjBAezkghv2FGpVCHelMTKqtSi/Pii0pzU4kOMpkB3T2SWEk3OB8Z+Xkm8oZmB qaGJmaWBqaWZsZI4r2dBR6KQQHpiSWp2ampBahFMHxMHp1QDU7vGtxd79Lf8M7Hn0bHmPbe9 XLXZ6VTM6v7+p8bGKx7dyt9+/8OO4LXcnWJlihpLI3hqlmT9Wf9ce2fRhn87unfP0m6uyJ7x UJpZccGujEl9jN57nzlq/5zcxX4g14dn8xs5ZX7pk4d2fei9ei4vbbZti4LpadGlYrovbl1Y ec+8WkRc6tW79c71P+Wb/RZdnqt/QLn66TEpT2eBfgnFj0vuH2P/vnlff77DHB7ZRXYrFRLm JNfanjdiemMePXX1A9+MWz++v3st963ipO+K6x8u77y9gneee8CRTNe98x7E6IT+vZlz4uDJ 4FW5jWfPTX7bLPta99n7D/YVO3LVEpbuL1TRLOb4/PPcmwlGV7crsRRnJBpqMRcVJwIApw64 5EUDAAA= X-CMS-MailID: 20231003111714eucas1p19c2351845d47b4ed1da8bc1b866e3353 X-Msg-Generator: CA X-RootMTR: 20231003111714eucas1p19c2351845d47b4ed1da8bc1b866e3353 X-EPHeader: CA CMS-TYPE: 201P X-CMS-RootMailID: 20231003111714eucas1p19c2351845d47b4ed1da8bc1b866e3353 References: <20231003111638.241542-1-m.majewski2@samsung.com> Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org Currently, each trip point defined in the device tree corresponds to a single hardware interrupt. This commit instead switches to using two hardware interrupts, whose values are set dynamically using the set_trips callback. Additionally, the critical temperature threshold is handled specifically. Setting interrupts in this way also fixes a long-standing lockdep warning, which was caused by calling thermal_zone_get_trips with our lock being held. Do note that this requires TMU initialization to be split into two parts, as done by the parent commit: parts of the initialization call into the thermal_zone_device structure and so must be done after its registration, but the initialization is also responsible for setting up calibration, which must be done before thermal_zone_device registration, which will call set_trips for the first time; if the calibration is not done in time, the interrupt values will be silently wrong! Signed-off-by: Mateusz Majewski --- v2 -> v3: Fixed formatting of some comments. v1 -> v2: We take clocks into account; anything that sets temperature thresholds needs clk. drivers/thermal/samsung/exynos_tmu.c | 400 +++++++++++++++------------ 1 file changed, 226 insertions(+), 174 deletions(-) diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index 343e27c61528..667bb18205fc 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -158,10 +158,12 @@ enum soc_type { * in the positive-TC generator block * 0 < reference_voltage <= 31 * @tzd: pointer to thermal_zone_device structure - * @ntrip: number of supported trip points. * @enabled: current status of TMU device - * @tmu_set_trip_temp: SoC specific method to set trip (rising threshold) - * @tmu_set_trip_hyst: SoC specific to set hysteresis (falling threshold) + * @tmu_set_low_temp: SoC specific method to set trip (falling threshold) + * @tmu_set_high_temp: SoC specific method to set trip (rising threshold) + * @tmu_set_crit_temp: SoC specific method to set critical temperature + * @tmu_disable_low: SoC specific method to disable an interrupt (falling threshold) + * @tmu_disable_high: SoC specific method to disable an interrupt (rising threshold) * @tmu_initialize: SoC specific TMU initialization method * @tmu_control: SoC specific TMU control method * @tmu_read: SoC specific TMU temperature read method @@ -183,13 +185,13 @@ struct exynos_tmu_data { u8 gain; u8 reference_voltage; struct thermal_zone_device *tzd; - unsigned int ntrip; bool enabled; - void (*tmu_set_trip_temp)(struct exynos_tmu_data *data, int trip, - u8 temp); - void (*tmu_set_trip_hyst)(struct exynos_tmu_data *data, int trip, - u8 temp, u8 hyst); + void (*tmu_set_low_temp)(struct exynos_tmu_data *data, u8 temp); + void (*tmu_set_high_temp)(struct exynos_tmu_data *data, u8 temp); + void (*tmu_set_crit_temp)(struct exynos_tmu_data *data, u8 temp); + void (*tmu_disable_low)(struct exynos_tmu_data *data); + void (*tmu_disable_high)(struct exynos_tmu_data *data); void (*tmu_initialize)(struct platform_device *pdev); void (*tmu_control)(struct platform_device *pdev, bool on); int (*tmu_read)(struct exynos_tmu_data *data); @@ -279,49 +281,28 @@ static int exynos_thermal_zone_configure(struct platform_device *pdev) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); struct thermal_zone_device *tzd = data->tzd; - int i, num_trips = thermal_zone_get_num_trips(tzd); - int ret = 0, temp; + int ret, temp; ret = thermal_zone_get_crit_temp(tzd, &temp); + if (ret) { + /* FIXME: Remove this special case */ + if (data->soc == SOC_ARCH_EXYNOS5433) + return 0; - if (ret && data->soc != SOC_ARCH_EXYNOS5433) { /* FIXME */ dev_err(&pdev->dev, "No CRITICAL trip point defined in device tree!\n"); - goto out; + return ret; } mutex_lock(&data->lock); - - if (num_trips > data->ntrip) { - dev_info(&pdev->dev, - "More trip points than supported by this TMU.\n"); - dev_info(&pdev->dev, - "%d trip points should be configured in polling mode.\n", - num_trips - data->ntrip); - } - clk_enable(data->clk); - num_trips = min_t(int, num_trips, data->ntrip); + data->tmu_set_crit_temp(data, temp / MCELSIUS); - /* Write temperature code for rising and falling threshold */ - for (i = 0; i < num_trips; i++) { - struct thermal_trip trip; - - ret = thermal_zone_get_trip(tzd, i, &trip); - if (ret) - goto err; - - data->tmu_set_trip_temp(data, i, trip.temperature / MCELSIUS); - data->tmu_set_trip_hyst(data, i, trip.temperature / MCELSIUS, - trip.hysteresis / MCELSIUS); - } - -err: clk_disable(data->clk); mutex_unlock(&data->lock); -out: - return ret; + + return 0; } static u32 get_con_reg(struct exynos_tmu_data *data, u32 con) @@ -354,17 +335,58 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on) mutex_unlock(&data->lock); } -static void exynos4210_tmu_set_trip_temp(struct exynos_tmu_data *data, - int trip_id, u8 temp) +static void exynos_tmu_update_bit(struct exynos_tmu_data *data, int reg_off, + int bit_off, bool enable) { - temp = temp_to_code(data, temp); - writeb(temp, data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + trip_id * 4); + u32 interrupt_en; + + interrupt_en = readl(data->base + reg_off); + if (enable) + interrupt_en |= 1 << bit_off; + else + interrupt_en &= ~(1 << bit_off); + writel(interrupt_en, data->base + reg_off); } -/* failing thresholds are not supported on Exynos4210 */ -static void exynos4210_tmu_set_trip_hyst(struct exynos_tmu_data *data, - int trip, u8 temp, u8 hyst) +static void exynos4210_tmu_set_low_temp(struct exynos_tmu_data *data, u8 temp) { + /* + * Failing thresholds are not supported on Exynos 4210. + * We use polling instead. + */ +} + +static void exynos4210_tmu_set_high_temp(struct exynos_tmu_data *data, u8 temp) +{ + temp = temp_to_code(data, temp); + writeb(temp, data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + 4); + exynos_tmu_update_bit(data, EXYNOS_TMU_REG_INTEN, + EXYNOS_TMU_INTEN_RISE0_SHIFT + 4, true); +} + +static void exynos4210_tmu_disable_low(struct exynos_tmu_data *data) +{ + /* Again, this is handled by polling. */ +} + +static void exynos4210_tmu_disable_high(struct exynos_tmu_data *data) +{ + exynos_tmu_update_bit(data, EXYNOS_TMU_REG_INTEN, + EXYNOS_TMU_INTEN_RISE0_SHIFT + 4, false); +} + +static void exynos4210_tmu_set_crit_temp(struct exynos_tmu_data *data, u8 temp) +{ + /* + * Hardware critical temperature handling is not supported on Exynos 4210. + * We still set the critical temperature threshold, but this is only to + * make sure it is handled as soon as possible. It is just a normal interrupt. + */ + + temp = temp_to_code(data, temp); + writeb(temp, data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + 12); + exynos_tmu_update_bit(data, EXYNOS_TMU_REG_INTEN, + EXYNOS_TMU_INTEN_RISE0_SHIFT + 12, true); } static void exynos4210_tmu_initialize(struct platform_device *pdev) @@ -376,33 +398,49 @@ static void exynos4210_tmu_initialize(struct platform_device *pdev) writeb(0, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP); } -static void exynos4412_tmu_set_trip_temp(struct exynos_tmu_data *data, - int trip, u8 temp) -{ - u32 th, con; - - th = readl(data->base + EXYNOS_THD_TEMP_RISE); - th &= ~(0xff << 8 * trip); - th |= temp_to_code(data, temp) << 8 * trip; - writel(th, data->base + EXYNOS_THD_TEMP_RISE); - - if (trip == 3) { - con = readl(data->base + EXYNOS_TMU_REG_CONTROL); - con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); - writel(con, data->base + EXYNOS_TMU_REG_CONTROL); - } -} - -static void exynos4412_tmu_set_trip_hyst(struct exynos_tmu_data *data, - int trip, u8 temp, u8 hyst) +static void exynos4412_tmu_set_low_temp(struct exynos_tmu_data *data, u8 temp) { u32 th; th = readl(data->base + EXYNOS_THD_TEMP_FALL); - th &= ~(0xff << 8 * trip); - if (hyst) - th |= temp_to_code(data, temp - hyst) << 8 * trip; + th &= ~(0xff << 0); + th |= temp_to_code(data, temp) << 0; writel(th, data->base + EXYNOS_THD_TEMP_FALL); + + exynos_tmu_update_bit(data, EXYNOS_TMU_REG_INTEN, + EXYNOS_TMU_INTEN_FALL0_SHIFT, true); +} + +static void exynos4412_tmu_set_high_temp(struct exynos_tmu_data *data, u8 temp) +{ + u32 th; + + th = readl(data->base + EXYNOS_THD_TEMP_RISE); + th &= ~(0xff << 8); + th |= temp_to_code(data, temp) << 8; + writel(th, data->base + EXYNOS_THD_TEMP_RISE); + + exynos_tmu_update_bit(data, EXYNOS_TMU_REG_INTEN, + EXYNOS_TMU_INTEN_RISE0_SHIFT + 4, true); +} + +static void exynos4412_tmu_disable_low(struct exynos_tmu_data *data) +{ + exynos_tmu_update_bit(data, EXYNOS_TMU_REG_INTEN, + EXYNOS_TMU_INTEN_FALL0_SHIFT, false); +} + +static void exynos4412_tmu_set_crit_temp(struct exynos_tmu_data *data, u8 temp) +{ + u32 th; + + th = readl(data->base + EXYNOS_THD_TEMP_RISE); + th &= ~(0xff << 24); + th |= temp_to_code(data, temp) << 24; + writel(th, data->base + EXYNOS_THD_TEMP_RISE); + + exynos_tmu_update_bit(data, EXYNOS_TMU_REG_CONTROL, + EXYNOS_TMU_THERM_TRIP_EN_SHIFT, true); } static void exynos4412_tmu_initialize(struct platform_device *pdev) @@ -432,44 +470,57 @@ static void exynos4412_tmu_initialize(struct platform_device *pdev) sanitize_temp_error(data, trim_info); } -static void exynos5433_tmu_set_trip_temp(struct exynos_tmu_data *data, - int trip, u8 temp) +static void exynos5433_tmu_set_low_temp(struct exynos_tmu_data *data, u8 temp) { - unsigned int reg_off, j; u32 th; - if (trip > 3) { - reg_off = EXYNOS5433_THD_TEMP_RISE7_4; - j = trip - 4; - } else { - reg_off = EXYNOS5433_THD_TEMP_RISE3_0; - j = trip; - } + th = readl(data->base + EXYNOS5433_THD_TEMP_FALL3_0); + th &= ~(0xff << 0); + th |= temp_to_code(data, temp) << 0; + writel(th, data->base + EXYNOS5433_THD_TEMP_FALL3_0); - th = readl(data->base + reg_off); - th &= ~(0xff << j * 8); - th |= (temp_to_code(data, temp) << j * 8); - writel(th, data->base + reg_off); + exynos_tmu_update_bit(data, EXYNOS5433_TMU_REG_INTEN, + EXYNOS_TMU_INTEN_FALL0_SHIFT, true); } -static void exynos5433_tmu_set_trip_hyst(struct exynos_tmu_data *data, - int trip, u8 temp, u8 hyst) +static void exynos5433_tmu_set_high_temp(struct exynos_tmu_data *data, u8 temp) { - unsigned int reg_off, j; u32 th; - if (trip > 3) { - reg_off = EXYNOS5433_THD_TEMP_FALL7_4; - j = trip - 4; - } else { - reg_off = EXYNOS5433_THD_TEMP_FALL3_0; - j = trip; - } + th = readl(data->base + EXYNOS5433_THD_TEMP_RISE3_0); + th &= ~(0xff << 8); + th |= temp_to_code(data, temp) << 8; + writel(th, data->base + EXYNOS5433_THD_TEMP_RISE3_0); - th = readl(data->base + reg_off); - th &= ~(0xff << j * 8); - th |= (temp_to_code(data, temp - hyst) << j * 8); - writel(th, data->base + reg_off); + exynos_tmu_update_bit(data, EXYNOS5433_TMU_REG_INTEN, + EXYNOS7_TMU_INTEN_RISE0_SHIFT + 1, true); +} + +static void exynos5433_tmu_disable_low(struct exynos_tmu_data *data) +{ + exynos_tmu_update_bit(data, EXYNOS5433_TMU_REG_INTEN, + EXYNOS_TMU_INTEN_FALL0_SHIFT, false); +} + +static void exynos5433_tmu_disable_high(struct exynos_tmu_data *data) +{ + exynos_tmu_update_bit(data, EXYNOS5433_TMU_REG_INTEN, + EXYNOS7_TMU_INTEN_RISE0_SHIFT + 1, false); +} + +static void exynos5433_tmu_set_crit_temp(struct exynos_tmu_data *data, u8 temp) +{ + u32 th; + + th = readl(data->base + EXYNOS5433_THD_TEMP_RISE7_4); + th &= ~(0xff << 24); + th |= temp_to_code(data, temp) << 24; + writel(th, data->base + EXYNOS5433_THD_TEMP_RISE7_4); + + exynos_tmu_update_bit(data, EXYNOS_TMU_REG_CONTROL, + EXYNOS_TMU_THERM_TRIP_EN_SHIFT, true); + exynos_tmu_update_bit(data, EXYNOS5433_TMU_REG_INTEN, + EXYNOS7_TMU_INTEN_RISE0_SHIFT + 7, true); } static void exynos5433_tmu_initialize(struct platform_device *pdev) @@ -505,34 +556,48 @@ static void exynos5433_tmu_initialize(struct platform_device *pdev) cal_type ? 2 : 1); } -static void exynos7_tmu_set_trip_temp(struct exynos_tmu_data *data, - int trip, u8 temp) +static void exynos7_tmu_update_temp(struct exynos_tmu_data *data, u8 temp, + int idx, bool rise) { unsigned int reg_off, bit_off; u32 th; + void __iomem *reg; - reg_off = ((7 - trip) / 2) * 4; - bit_off = ((8 - trip) % 2); + reg_off = ((7 - idx) / 2) * 4; + bit_off = ((8 - idx) % 2); - th = readl(data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); + reg = data->base + + (rise ? EXYNOS7_THD_TEMP_RISE7_6 : EXYNOS7_THD_TEMP_FALL7_6) + + reg_off; + th = readl(reg); th &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); th |= temp_to_code(data, temp) << (16 * bit_off); - writel(th, data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); + writel(th, reg); + + exynos_tmu_update_bit(data, EXYNOS5433_TMU_REG_INTEN, + (rise ? EXYNOS7_TMU_INTEN_RISE0_SHIFT : + EXYNOS_TMU_INTEN_FALL0_SHIFT) + + idx, + true); } -static void exynos7_tmu_set_trip_hyst(struct exynos_tmu_data *data, - int trip, u8 temp, u8 hyst) +static void exynos7_tmu_set_low_temp(struct exynos_tmu_data *data, u8 temp) { - unsigned int reg_off, bit_off; - u32 th; + exynos7_tmu_update_temp(data, temp, 0, false); +} - reg_off = ((7 - trip) / 2) * 4; - bit_off = ((8 - trip) % 2); +static void exynos7_tmu_set_high_temp(struct exynos_tmu_data *data, u8 temp) +{ + exynos7_tmu_update_temp(data, temp, 1, true); +} - th = readl(data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off); - th &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); - th |= temp_to_code(data, temp - hyst) << (16 * bit_off); - writel(th, data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off); +static void exynos7_tmu_set_crit_temp(struct exynos_tmu_data *data, u8 temp) +{ + /* + * Like Exynos 4210, Exynos 7 does not seem to support critical temperature + * handling in hardware. Again, we still set a separate interrupt for it. + */ + exynos7_tmu_update_temp(data, temp, 7, true); } static void exynos7_tmu_initialize(struct platform_device *pdev) @@ -547,87 +612,44 @@ static void exynos7_tmu_initialize(struct platform_device *pdev) static void exynos4210_tmu_control(struct platform_device *pdev, bool on) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); - struct thermal_zone_device *tz = data->tzd; - struct thermal_trip trip; - unsigned int con, interrupt_en = 0, i; + unsigned int con; con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); - if (on) { - for (i = 0; i < data->ntrip; i++) { - if (thermal_zone_get_trip(tz, i, &trip)) - continue; - - interrupt_en |= - (1 << (EXYNOS_TMU_INTEN_RISE0_SHIFT + i * 4)); - } - - if (data->soc != SOC_ARCH_EXYNOS4210) - interrupt_en |= - interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; - + if (on) con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); - } else { + else con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); - } - writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN); writel(con, data->base + EXYNOS_TMU_REG_CONTROL); } static void exynos5433_tmu_control(struct platform_device *pdev, bool on) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); - struct thermal_zone_device *tz = data->tzd; - struct thermal_trip trip; - unsigned int con, interrupt_en = 0, pd_det_en, i; + unsigned int con, pd_det_en; con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); - if (on) { - for (i = 0; i < data->ntrip; i++) { - if (thermal_zone_get_trip(tz, i, &trip)) - continue; - - interrupt_en |= - (1 << (EXYNOS7_TMU_INTEN_RISE0_SHIFT + i)); - } - - interrupt_en |= - interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; - + if (on) con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); - } else + else con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); pd_det_en = on ? EXYNOS5433_PD_DET_EN : 0; writel(pd_det_en, data->base + EXYNOS5433_TMU_PD_DET_EN); - writel(interrupt_en, data->base + EXYNOS5433_TMU_REG_INTEN); writel(con, data->base + EXYNOS_TMU_REG_CONTROL); } static void exynos7_tmu_control(struct platform_device *pdev, bool on) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); - struct thermal_zone_device *tz = data->tzd; - struct thermal_trip trip; - unsigned int con, interrupt_en = 0, i; + unsigned int con; con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); if (on) { - for (i = 0; i < data->ntrip; i++) { - if (thermal_zone_get_trip(tz, i, &trip)) - continue; - - interrupt_en |= - (1 << (EXYNOS7_TMU_INTEN_RISE0_SHIFT + i)); - } - - interrupt_en |= - interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; - con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); con |= (1 << EXYNOS7_PD_DET_EN_SHIFT); } else { @@ -635,7 +657,6 @@ static void exynos7_tmu_control(struct platform_device *pdev, bool on) con &= ~(1 << EXYNOS7_PD_DET_EN_SHIFT); } - writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN); writel(con, data->base + EXYNOS_TMU_REG_CONTROL); } @@ -873,13 +894,15 @@ static int exynos_map_dt_data(struct platform_device *pdev) switch (data->soc) { case SOC_ARCH_EXYNOS4210: - data->tmu_set_trip_temp = exynos4210_tmu_set_trip_temp; - data->tmu_set_trip_hyst = exynos4210_tmu_set_trip_hyst; + data->tmu_set_low_temp = exynos4210_tmu_set_low_temp; + data->tmu_set_high_temp = exynos4210_tmu_set_high_temp; + data->tmu_disable_low = exynos4210_tmu_disable_low; + data->tmu_disable_high = exynos4210_tmu_disable_high; + data->tmu_set_crit_temp = exynos4210_tmu_set_crit_temp; data->tmu_initialize = exynos4210_tmu_initialize; data->tmu_control = exynos4210_tmu_control; data->tmu_read = exynos4210_tmu_read; data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; - data->ntrip = 4; data->gain = 15; data->reference_voltage = 7; data->efuse_value = 55; @@ -892,14 +915,16 @@ static int exynos_map_dt_data(struct platform_device *pdev) case SOC_ARCH_EXYNOS5260: case SOC_ARCH_EXYNOS5420: case SOC_ARCH_EXYNOS5420_TRIMINFO: - data->tmu_set_trip_temp = exynos4412_tmu_set_trip_temp; - data->tmu_set_trip_hyst = exynos4412_tmu_set_trip_hyst; + data->tmu_set_low_temp = exynos4412_tmu_set_low_temp; + data->tmu_set_high_temp = exynos4412_tmu_set_high_temp; + data->tmu_disable_low = exynos4412_tmu_disable_low; + data->tmu_disable_high = exynos4210_tmu_disable_high; + data->tmu_set_crit_temp = exynos4412_tmu_set_crit_temp; data->tmu_initialize = exynos4412_tmu_initialize; data->tmu_control = exynos4210_tmu_control; data->tmu_read = exynos4412_tmu_read; data->tmu_set_emulation = exynos4412_tmu_set_emulation; data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; - data->ntrip = 4; data->gain = 8; data->reference_voltage = 16; data->efuse_value = 55; @@ -911,14 +936,16 @@ static int exynos_map_dt_data(struct platform_device *pdev) data->max_efuse_value = 100; break; case SOC_ARCH_EXYNOS5433: - data->tmu_set_trip_temp = exynos5433_tmu_set_trip_temp; - data->tmu_set_trip_hyst = exynos5433_tmu_set_trip_hyst; + data->tmu_set_low_temp = exynos5433_tmu_set_low_temp; + data->tmu_set_high_temp = exynos5433_tmu_set_high_temp; + data->tmu_disable_low = exynos5433_tmu_disable_low; + data->tmu_disable_high = exynos5433_tmu_disable_high; + data->tmu_set_crit_temp = exynos5433_tmu_set_crit_temp; data->tmu_initialize = exynos5433_tmu_initialize; data->tmu_control = exynos5433_tmu_control; data->tmu_read = exynos4412_tmu_read; data->tmu_set_emulation = exynos4412_tmu_set_emulation; data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; - data->ntrip = 8; data->gain = 8; if (res.start == EXYNOS5433_G3D_BASE) data->reference_voltage = 23; @@ -929,14 +956,16 @@ static int exynos_map_dt_data(struct platform_device *pdev) data->max_efuse_value = 150; break; case SOC_ARCH_EXYNOS7: - data->tmu_set_trip_temp = exynos7_tmu_set_trip_temp; - data->tmu_set_trip_hyst = exynos7_tmu_set_trip_hyst; + data->tmu_set_low_temp = exynos7_tmu_set_low_temp; + data->tmu_set_high_temp = exynos7_tmu_set_high_temp; + data->tmu_disable_low = exynos5433_tmu_disable_low; + data->tmu_disable_high = exynos5433_tmu_disable_high; + data->tmu_set_crit_temp = exynos7_tmu_set_crit_temp; data->tmu_initialize = exynos7_tmu_initialize; data->tmu_control = exynos7_tmu_control; data->tmu_read = exynos7_tmu_read; data->tmu_set_emulation = exynos4412_tmu_set_emulation; data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; - data->ntrip = 8; data->gain = 9; data->reference_voltage = 17; data->efuse_value = 75; @@ -972,9 +1001,32 @@ static int exynos_map_dt_data(struct platform_device *pdev) return 0; } +static int exynos_set_trips(struct thermal_zone_device *tz, int low, int high) +{ + struct exynos_tmu_data *data = thermal_zone_device_priv(tz); + + mutex_lock(&data->lock); + clk_enable(data->clk); + + if (low > INT_MIN) + data->tmu_set_low_temp(data, low / MCELSIUS); + else + data->tmu_disable_low(data); + if (high < INT_MAX) + data->tmu_set_high_temp(data, high / MCELSIUS); + else + data->tmu_disable_high(data); + + clk_disable(data->clk); + mutex_unlock(&data->lock); + + return 0; +} + static const struct thermal_zone_device_ops exynos_sensor_ops = { .get_temp = exynos_get_temp, .set_emul_temp = exynos_tmu_set_emulation, + .set_trips = exynos_set_trips, }; static int exynos_tmu_probe(struct platform_device *pdev)